mirror of
https://github.com/systemd/systemd
synced 2026-04-23 07:24:51 +02:00
Compare commits
18 Commits
af5ee76c56
...
592d576552
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
592d576552 | ||
|
|
5f1077af1e | ||
|
|
d0b4f13ef8 | ||
|
|
9eb814818d | ||
|
|
52bb308c13 | ||
|
|
de0988f9d2 | ||
|
|
7d469b63a1 | ||
|
|
1a34f913a6 | ||
|
|
b7df2c78ea | ||
|
|
e94830c0a1 | ||
|
|
f620a36865 | ||
|
|
1fe368e526 | ||
|
|
d23b3bfdd6 | ||
|
|
cf5d9598b6 | ||
|
|
20ec8f534f | ||
|
|
1011935785 | ||
|
|
f65a33269e | ||
|
|
09d4d60360 |
4
TODO
4
TODO
@ -1129,6 +1129,10 @@ Features:
|
|||||||
- teach it to prepare an ESP wholesale, i.e. with mkfs.vfat invocation
|
- teach it to prepare an ESP wholesale, i.e. with mkfs.vfat invocation
|
||||||
- teach it to copy in unified kernel images and maybe type #1 boot loader spec entries from host
|
- teach it to copy in unified kernel images and maybe type #1 boot loader spec entries from host
|
||||||
- make it operate on loopback files, dissecting enough to find ESP to operate on
|
- make it operate on loopback files, dissecting enough to find ESP to operate on
|
||||||
|
- bootspec: properly support boot attempt counters when parsing entry file names
|
||||||
|
|
||||||
|
* kernel-install:
|
||||||
|
- optionally, support generating type #2 entries instead of type #1, including signing them
|
||||||
|
|
||||||
* logind:
|
* logind:
|
||||||
- logind: optionally, ignore idle-hint logic for autosuspend, block suspend as long as a session is around
|
- logind: optionally, ignore idle-hint logic for autosuspend, block suspend as long as a session is around
|
||||||
|
|||||||
@ -232,6 +232,16 @@ spaces from its value. The following keys are known:
|
|||||||
other installed operating systems. This ID shall be formatted as 32 lower
|
other installed operating systems. This ID shall be formatted as 32 lower
|
||||||
case hexadecimal characters (i.e. without any UUID formatting). This key is
|
case hexadecimal characters (i.e. without any UUID formatting). This key is
|
||||||
optional. Example: `4098b3f648d74c13b1f04ccfba7798e8`.
|
optional. Example: `4098b3f648d74c13b1f04ccfba7798e8`.
|
||||||
|
* `sort-key` shall contain a short string used for sorting entries on
|
||||||
|
display. This can be defined freely though should typically be initialized
|
||||||
|
from `IMAGE_ID=` or `ID=` from `/etc/os-release` of the relevant entry,
|
||||||
|
possibly suffixed. This field is optional. If set, it is used as primary
|
||||||
|
sorting key for the entries on display (lexicographically increasing). It
|
||||||
|
does not have to be unique (and usually is not). If non-unique the the
|
||||||
|
`machine-id` (lexicographically increasing) and `version` (lexicographically
|
||||||
|
decreasing, i.e. newest version first) fields described above are used as
|
||||||
|
secondary/ternary sorting keys. If this field is not set entries are
|
||||||
|
typically sorted by the `.conf` file name of the entry.
|
||||||
* `linux` refers to the Linux kernel to spawn and shall be a path relative to
|
* `linux` refers to the Linux kernel to spawn and shall be a path relative to
|
||||||
`$BOOT`. It is recommended that every distribution creates a machine id and
|
`$BOOT`. It is recommended that every distribution creates a machine id and
|
||||||
version specific subdirectory below `$BOOT` and places its kernels and
|
version specific subdirectory below `$BOOT` and places its kernels and
|
||||||
@ -269,8 +279,9 @@ key and is otherwise not valid. Here's an example for a complete drop-in file:
|
|||||||
|
|
||||||
# /boot/loader/entries/6a9857a393724b7a981ebb5b8495b9ea-3.8.0-2.fc19.x86_64.conf
|
# /boot/loader/entries/6a9857a393724b7a981ebb5b8495b9ea-3.8.0-2.fc19.x86_64.conf
|
||||||
title Fedora 19 (Rawhide)
|
title Fedora 19 (Rawhide)
|
||||||
version 3.8.0-2.fc19.x86_64
|
sort-key fedora
|
||||||
machine-id 6a9857a393724b7a981ebb5b8495b9ea
|
machine-id 6a9857a393724b7a981ebb5b8495b9ea
|
||||||
|
version 3.8.0-2.fc19.x86_64
|
||||||
options root=UUID=6d3376e4-fc93-4509-95ec-a21d68011da2
|
options root=UUID=6d3376e4-fc93-4509-95ec-a21d68011da2
|
||||||
architecture x64
|
architecture x64
|
||||||
linux /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/linux
|
linux /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/linux
|
||||||
@ -358,10 +369,10 @@ simply reads all files `$BOOT/loader/entries/*.conf`, and populates its boot
|
|||||||
menu with this. On EFI, it then extends this with any unified kernel images
|
menu with this. On EFI, it then extends this with any unified kernel images
|
||||||
found in `$BOOT/EFI/Linux/*.efi`. It may also add additional entries, for
|
found in `$BOOT/EFI/Linux/*.efi`. It may also add additional entries, for
|
||||||
example a "Reboot into firmware" option. Optionally it may sort the menu based
|
example a "Reboot into firmware" option. Optionally it may sort the menu based
|
||||||
on the `machine-id` and `version` fields, and possibly others. It uses the file
|
on the `sort-key`, `machine-id` and `version` fields, and possibly others. It
|
||||||
name to identify specific items, for example in case it supports storing away
|
uses the file name to identify specific items, for example in case it supports
|
||||||
default entry information somewhere. A boot loader should generally not modify
|
storing away default entry information somewhere. A boot loader should
|
||||||
these files.
|
generally not modify these files.
|
||||||
|
|
||||||
For "Boot Loader Specification Entries" (Type #1), the _kernel package
|
For "Boot Loader Specification Entries" (Type #1), the _kernel package
|
||||||
installer_ installs the kernel and initrd images to `$BOOT` (it is recommended
|
installer_ installs the kernel and initrd images to `$BOOT` (it is recommended
|
||||||
|
|||||||
@ -283,11 +283,12 @@ pre-defined purposes between Linux, generic low-level distributions and
|
|||||||
ranges.
|
ranges.
|
||||||
|
|
||||||
Note that the range 2147483648…4294967294 (i.e. 2^31…2^32-2) should be handled
|
Note that the range 2147483648…4294967294 (i.e. 2^31…2^32-2) should be handled
|
||||||
with care. Various programs (including kernel file systems, see `devpts`) have
|
with care. Various programs (including kernel file systems — see `devpts` — or
|
||||||
trouble with UIDs outside of the signed 32bit range, i.e any UIDs equal to or
|
even kernel syscalls – see `setfsuid()`) have trouble with UIDs outside of the
|
||||||
above 2147483648. It is thus strongly recommended to stay away from this range
|
signed 32bit range, i.e any UIDs equal to or above 2147483648. It is thus
|
||||||
in order to avoid complications. This range should be considered reserved for
|
strongly recommended to stay away from this range in order to avoid
|
||||||
future, special purposes.
|
complications. This range should be considered reserved for future, special
|
||||||
|
purposes.
|
||||||
|
|
||||||
## Notes on resolvability of user and group names
|
## Notes on resolvability of user and group names
|
||||||
|
|
||||||
|
|||||||
@ -602,6 +602,18 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP*G60*Notebook*PC:*
|
|||||||
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*2570p*:*
|
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*2570p*:*
|
||||||
KEYBOARD_KEY_f8=wlan # Wireless HW switch button
|
KEYBOARD_KEY_f8=wlan # Wireless HW switch button
|
||||||
|
|
||||||
|
# Elitebook 2760p
|
||||||
|
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*2760p*:*
|
||||||
|
KEYBOARD_KEY_89=battery # Fn+F8
|
||||||
|
KEYBOARD_KEY_f8=unknown # rfkill is also reported by HP Wireless hotkeys
|
||||||
|
KEYBOARD_KEY_86=volumeup
|
||||||
|
KEYBOARD_KEY_87=volumedown
|
||||||
|
KEYBOARD_KEY_92=brightnessdown
|
||||||
|
KEYBOARD_KEY_97=brightnessup
|
||||||
|
KEYBOARD_KEY_d8=!f23 # touchpad off
|
||||||
|
KEYBOARD_KEY_d9=!f22 # touchpad on
|
||||||
|
KEYBOARD_KEY_b3=unknown # FIXME: Auto brightness
|
||||||
|
|
||||||
# TX2
|
# TX2
|
||||||
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*[tT][xX]2*:*
|
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*[tT][xX]2*:*
|
||||||
KEYBOARD_KEY_c2=media
|
KEYBOARD_KEY_c2=media
|
||||||
|
|||||||
@ -501,10 +501,10 @@
|
|||||||
considered 'good' from then on.</para>
|
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'
|
<para>The boot menu takes the 'tries left' counter into account when sorting the menu entries: entries in 'bad'
|
||||||
state are ordered towards the end of the list, and entries in 'good' or 'indeterminate' towards the beginning.
|
state are ordered at the beginning of the list, and entries in 'good' or 'indeterminate' at the end. The user can
|
||||||
The user can freely choose to boot any entry of the menu, including those already marked 'bad'. If the menu entry
|
freely choose to boot any entry of the menu, including those already marked 'bad'. If the menu entry to boot is
|
||||||
to boot is automatically determined, this means that 'good' or 'indeterminate' entries are generally preferred as
|
automatically determined, this means that 'good' or 'indeterminate' entries are generally preferred (as the bottom
|
||||||
boot entries are tried in sort order, and 'bad' entries will only be considered if there are no 'good' or
|
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>
|
'indeterminate' entries left.</para>
|
||||||
|
|
||||||
<para>The <citerefentry><refentrytitle>kernel-install</refentrytitle><manvolnum>8</manvolnum></citerefentry> kernel
|
<para>The <citerefentry><refentrytitle>kernel-install</refentrytitle><manvolnum>8</manvolnum></citerefentry> kernel
|
||||||
|
|||||||
@ -40,6 +40,7 @@ items = [['busctl', ''],
|
|||||||
['loginctl', 'ENABLE_LOGIND'],
|
['loginctl', 'ENABLE_LOGIND'],
|
||||||
['machinectl', 'ENABLE_MACHINED'],
|
['machinectl', 'ENABLE_MACHINED'],
|
||||||
['networkctl', 'ENABLE_NETWORKD'],
|
['networkctl', 'ENABLE_NETWORKD'],
|
||||||
|
['oomctl', 'ENABLE_OOMD'],
|
||||||
['portablectl', 'ENABLE_PORTABLED'],
|
['portablectl', 'ENABLE_PORTABLED'],
|
||||||
['resolvectl', 'ENABLE_RESOLVE'],
|
['resolvectl', 'ENABLE_RESOLVE'],
|
||||||
['systemd-resolve', 'ENABLE_RESOLVE'],
|
['systemd-resolve', 'ENABLE_RESOLVE'],
|
||||||
|
|||||||
57
shell-completion/bash/oomctl
Normal file
57
shell-completion/bash/oomctl
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# oomctl(1) completion -*- shell-script -*-
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# systemd is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
|
# along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
__contains_word () {
|
||||||
|
local w word=$1; shift
|
||||||
|
for w in "$@"; do
|
||||||
|
[[ $w = "$word" ]] && return
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
_oomctl() {
|
||||||
|
local i verb comps
|
||||||
|
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
|
||||||
|
local OPTS='-h --help --version --no-pager'
|
||||||
|
|
||||||
|
if [[ "$cur" = -* ]]; then
|
||||||
|
COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") )
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local -A VERBS=(
|
||||||
|
[STANDALONE]='help dump'
|
||||||
|
)
|
||||||
|
|
||||||
|
for ((i=0; i < COMP_CWORD; i++)); do
|
||||||
|
if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]}; then
|
||||||
|
verb=${COMP_WORDS[i]}
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z ${verb-} ]]; then
|
||||||
|
comps=${VERBS[*]}
|
||||||
|
elif __contains_word "$verb" ${VERBS[STANDALONE]}; then
|
||||||
|
comps=''
|
||||||
|
fi
|
||||||
|
|
||||||
|
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _oomctl oomctl
|
||||||
28
shell-completion/zsh/_oomctl
Normal file
28
shell-completion/zsh/_oomctl
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#compdef oomctl
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
|
||||||
|
(( $+functions[_oomctl_commands] )) || _oomctl_commands()
|
||||||
|
{
|
||||||
|
local -a _oomctl_cmds
|
||||||
|
_oomctl_cmds=(
|
||||||
|
"dump:Show the current state of the cgroup(s) and system context(s)"
|
||||||
|
"help:Prints a short help text and exits."
|
||||||
|
)
|
||||||
|
if (( CURRENT == 1 )); then
|
||||||
|
_describe -t commands 'oomctl command' _oomctl_cmds
|
||||||
|
else
|
||||||
|
local curcontext="$curcontext"
|
||||||
|
cmd="${${_oomctl_cmds[(r)$words[1]:*]%%:*}}"
|
||||||
|
if (( $+functions[_oomctl_$cmd] )); then
|
||||||
|
_oomctl_$cmd
|
||||||
|
else
|
||||||
|
_message "no more options"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_arguments \
|
||||||
|
{-h,--help}'[Prints a short help text and exits.]' \
|
||||||
|
'--version[Prints a short version string and exits.]' \
|
||||||
|
'--no-pager[Do not pipe output into a pager]' \
|
||||||
|
'*::oomctl command:_oomctl_commands'
|
||||||
@ -34,6 +34,7 @@ items = [['_busctl', ''],
|
|||||||
['_loginctl', 'ENABLE_LOGIND'],
|
['_loginctl', 'ENABLE_LOGIND'],
|
||||||
['_machinectl', 'ENABLE_MACHINED'],
|
['_machinectl', 'ENABLE_MACHINED'],
|
||||||
['_networkctl', 'ENABLE_NETWORKD'],
|
['_networkctl', 'ENABLE_NETWORKD'],
|
||||||
|
['_oomctl', 'ENABLE_OOMD'],
|
||||||
['_systemd-inhibit', 'ENABLE_LOGIND'],
|
['_systemd-inhibit', 'ENABLE_LOGIND'],
|
||||||
['_resolvectl', 'ENABLE_RESOLVE'],
|
['_resolvectl', 'ENABLE_RESOLVE'],
|
||||||
['_systemd-tmpfiles', 'ENABLE_TMPFILES'],
|
['_systemd-tmpfiles', 'ENABLE_TMPFILES'],
|
||||||
|
|||||||
@ -159,7 +159,6 @@ int pipe_eof(int fd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) {
|
int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) {
|
||||||
struct timespec ts;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(fds || nfds == 0);
|
assert(fds || nfds == 0);
|
||||||
@ -167,7 +166,7 @@ int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) {
|
|||||||
if (nfds == 0)
|
if (nfds == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
r = ppoll(fds, nfds, timeout == USEC_INFINITY ? NULL : timespec_store(&ts, timeout), NULL);
|
r = ppoll(fds, nfds, timeout == USEC_INFINITY ? NULL : TIMESPEC_STORE(timeout), NULL);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
|
|||||||
@ -820,13 +820,12 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) {
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
usec_t n;
|
usec_t n;
|
||||||
siginfo_t status = {};
|
siginfo_t status = {};
|
||||||
struct timespec ts;
|
|
||||||
|
|
||||||
n = now(CLOCK_MONOTONIC);
|
n = now(CLOCK_MONOTONIC);
|
||||||
if (n >= until)
|
if (n >= until)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
r = RET_NERRNO(sigtimedwait(&mask, NULL, timespec_store(&ts, until - n)));
|
r = RET_NERRNO(sigtimedwait(&mask, NULL, TIMESPEC_STORE(until - n)));
|
||||||
/* Assuming we woke due to the child exiting. */
|
/* Assuming we woke due to the child exiting. */
|
||||||
if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) {
|
if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) {
|
||||||
if (status.si_pid == pid) {
|
if (status.si_pid == pid) {
|
||||||
|
|||||||
@ -70,10 +70,9 @@ char* strv_find_startswith(char * const *l, const char *name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
char** strv_free(char **l) {
|
char** strv_free(char **l) {
|
||||||
if (!l)
|
char **k;
|
||||||
return NULL;
|
|
||||||
|
|
||||||
for (char **k = l; *k; k++)
|
STRV_FOREACH(k, l)
|
||||||
free(*k);
|
free(*k);
|
||||||
|
|
||||||
return mfree(l);
|
return mfree(l);
|
||||||
@ -89,32 +88,30 @@ char** strv_free_erase(char **l) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
char** strv_copy(char * const *l) {
|
char** strv_copy(char * const *l) {
|
||||||
char **r, **k;
|
_cleanup_strv_free_ char **result = NULL;
|
||||||
|
char **k, * const *i;
|
||||||
|
|
||||||
k = r = new(char*, strv_length(l) + 1);
|
result = new(char*, strv_length(l) + 1);
|
||||||
if (!r)
|
if (!result)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (l)
|
k = result;
|
||||||
for (; *l; k++, l++) {
|
STRV_FOREACH(i, l) {
|
||||||
*k = strdup(*l);
|
*k = strdup(*i);
|
||||||
if (!*k) {
|
if (!*k)
|
||||||
strv_free(r);
|
return NULL;
|
||||||
return NULL;
|
k++;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
*k = NULL;
|
*k = NULL;
|
||||||
return r;
|
return TAKE_PTR(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t strv_length(char * const *l) {
|
size_t strv_length(char * const *l) {
|
||||||
|
char * const *i;
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
|
|
||||||
if (!l)
|
STRV_FOREACH(i, l)
|
||||||
return 0;
|
|
||||||
|
|
||||||
for (; *l; l++)
|
|
||||||
n++;
|
n++;
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
|
|||||||
@ -115,9 +115,13 @@ nsec_t timespec_load_nsec(const struct timespec *ts) _pure_;
|
|||||||
struct timespec* timespec_store(struct timespec *ts, usec_t u);
|
struct timespec* timespec_store(struct timespec *ts, usec_t u);
|
||||||
struct timespec* timespec_store_nsec(struct timespec *ts, nsec_t n);
|
struct timespec* timespec_store_nsec(struct timespec *ts, nsec_t n);
|
||||||
|
|
||||||
|
#define TIMESPEC_STORE(u) timespec_store(&(struct timespec) {}, (u))
|
||||||
|
|
||||||
usec_t timeval_load(const struct timeval *tv) _pure_;
|
usec_t timeval_load(const struct timeval *tv) _pure_;
|
||||||
struct timeval* timeval_store(struct timeval *tv, usec_t u);
|
struct timeval* timeval_store(struct timeval *tv, usec_t u);
|
||||||
|
|
||||||
|
#define TIMEVAL_STORE(u) timeval_store(&(struct timeval) {}, (u))
|
||||||
|
|
||||||
char* format_timestamp_style(char *buf, size_t l, usec_t t, TimestampStyle style) _warn_unused_result_;
|
char* format_timestamp_style(char *buf, size_t l, usec_t t, TimestampStyle style) _warn_unused_result_;
|
||||||
char* format_timestamp_relative(char *buf, size_t l, usec_t t) _warn_unused_result_;
|
char* format_timestamp_relative(char *buf, size_t l, usec_t t) _warn_unused_result_;
|
||||||
char* format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) _warn_unused_result_;
|
char* format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) _warn_unused_result_;
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
#include "efi-loader.h"
|
#include "efi-loader.h"
|
||||||
#include "efivars.h"
|
#include "efivars.h"
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
|
#include "find-esp.h"
|
||||||
#include "fs-util.h"
|
#include "fs-util.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "main-func.h"
|
#include "main-func.h"
|
||||||
|
|||||||
@ -24,6 +24,7 @@
|
|||||||
#include "escape.h"
|
#include "escape.h"
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "fileio.h"
|
#include "fileio.h"
|
||||||
|
#include "find-esp.h"
|
||||||
#include "fs-util.h"
|
#include "fs-util.h"
|
||||||
#include "glyph-util.h"
|
#include "glyph-util.h"
|
||||||
#include "main-func.h"
|
#include "main-func.h"
|
||||||
@ -590,6 +591,8 @@ static int boot_entry_show(
|
|||||||
|
|
||||||
printf(" source: %s\n", link ?: e->path);
|
printf(" source: %s\n", link ?: e->path);
|
||||||
}
|
}
|
||||||
|
if (e->sort_key)
|
||||||
|
printf(" sort-key: %s\n", e->sort_key);
|
||||||
if (e->version)
|
if (e->version)
|
||||||
printf(" version: %s\n", e->version);
|
printf(" version: %s\n", e->version);
|
||||||
if (e->machine_id)
|
if (e->machine_id)
|
||||||
|
|||||||
@ -53,6 +53,7 @@ typedef struct {
|
|||||||
CHAR16 *id; /* The unique identifier for this entry (typically the filename of the file defining the entry) */
|
CHAR16 *id; /* The unique identifier for this entry (typically the filename of the file defining the entry) */
|
||||||
CHAR16 *title_show; /* The string to actually display (this is made unique before showing) */
|
CHAR16 *title_show; /* The string to actually display (this is made unique before showing) */
|
||||||
CHAR16 *title; /* The raw (human readable) title string of the entry (not necessarily unique) */
|
CHAR16 *title; /* The raw (human readable) title string of the entry (not necessarily unique) */
|
||||||
|
CHAR16 *sort_key; /* The string to use as primary sory key, usually ID= from os-release, possibly suffixed */
|
||||||
CHAR16 *version; /* The raw (human readable) version string of the entry */
|
CHAR16 *version; /* The raw (human readable) version string of the entry */
|
||||||
CHAR16 *machine_id;
|
CHAR16 *machine_id;
|
||||||
EFI_HANDLE *device;
|
EFI_HANDLE *device;
|
||||||
@ -428,6 +429,17 @@ static CHAR16 *update_timeout_efivar(UINT32 *t, BOOLEAN inc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static BOOLEAN unicode_supported(void) {
|
||||||
|
static INTN cache = -1;
|
||||||
|
|
||||||
|
if (cache < 0)
|
||||||
|
/* Basic unicode box drawing support is mandated by the spec, but it does
|
||||||
|
* not hurt to make sure it works. */
|
||||||
|
cache = !EFI_ERROR(ST->ConOut->TestString(ST->ConOut, (CHAR16 *) L"─"));
|
||||||
|
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
static void ps_string(const CHAR16 *fmt, const void *value) {
|
static void ps_string(const CHAR16 *fmt, const void *value) {
|
||||||
assert(fmt);
|
assert(fmt);
|
||||||
if (value)
|
if (value)
|
||||||
@ -443,7 +455,11 @@ static BOOLEAN ps_continue(void) {
|
|||||||
UINT64 key;
|
UINT64 key;
|
||||||
EFI_STATUS err;
|
EFI_STATUS err;
|
||||||
|
|
||||||
Print(L"\n--- Press any key to continue, ESC or q to quit. ---\n\n");
|
if (unicode_supported())
|
||||||
|
Print(L"\n─── Press any key to continue, ESC or q to quit. ───\n\n");
|
||||||
|
else
|
||||||
|
Print(L"\n--- Press any key to continue, ESC or q to quit. ---\n\n");
|
||||||
|
|
||||||
err = console_key_read(&key, UINT64_MAX);
|
err = console_key_read(&key, UINT64_MAX);
|
||||||
return !EFI_ERROR(err) && !IN_SET(key, KEYPRESS(0, SCAN_ESC, 0), KEYPRESS(0, 0, 'q'), KEYPRESS(0, 0, 'Q'));
|
return !EFI_ERROR(err) && !IN_SET(key, KEYPRESS(0, SCAN_ESC, 0), KEYPRESS(0, 0, 'q'), KEYPRESS(0, 0, 'Q'));
|
||||||
}
|
}
|
||||||
@ -540,6 +556,7 @@ static void print_status(Config *config, CHAR16 *loaded_image_path) {
|
|||||||
ps_string(L" id: %s\n", entry->id);
|
ps_string(L" id: %s\n", entry->id);
|
||||||
ps_string(L" title: %s\n", entry->title);
|
ps_string(L" title: %s\n", entry->title);
|
||||||
ps_string(L" title show: %s\n", streq_ptr(entry->title, entry->title_show) ? NULL : entry->title_show);
|
ps_string(L" title show: %s\n", streq_ptr(entry->title, entry->title_show) ? NULL : entry->title_show);
|
||||||
|
ps_string(L" sort key: %s\n", entry->sort_key);
|
||||||
ps_string(L" version: %s\n", entry->version);
|
ps_string(L" version: %s\n", entry->version);
|
||||||
ps_string(L" machine-id: %s\n", entry->machine_id);
|
ps_string(L" machine-id: %s\n", entry->machine_id);
|
||||||
if (entry->device)
|
if (entry->device)
|
||||||
@ -598,7 +615,7 @@ static BOOLEAN menu_run(
|
|||||||
UINTN x_start = 0, y_start = 0, y_status = 0;
|
UINTN x_start = 0, y_start = 0, y_status = 0;
|
||||||
UINTN x_max, y_max;
|
UINTN x_max, y_max;
|
||||||
_cleanup_(strv_freep) CHAR16 **lines = NULL;
|
_cleanup_(strv_freep) CHAR16 **lines = NULL;
|
||||||
_cleanup_freepool_ CHAR16 *clearline = NULL, *status = NULL;
|
_cleanup_freepool_ CHAR16 *clearline = NULL, *separator = NULL, *status = NULL;
|
||||||
UINT32 timeout_efivar_saved = config->timeout_sec_efivar;
|
UINT32 timeout_efivar_saved = config->timeout_sec_efivar;
|
||||||
UINT32 timeout_remain = config->timeout_sec == TIMEOUT_MENU_FORCE ? 0 : config->timeout_sec;
|
UINT32 timeout_remain = config->timeout_sec == TIMEOUT_MENU_FORCE ? 0 : config->timeout_sec;
|
||||||
BOOLEAN exit = FALSE, run = TRUE, firmware_setup = FALSE;
|
BOOLEAN exit = FALSE, run = TRUE, firmware_setup = FALSE;
|
||||||
@ -619,12 +636,11 @@ static BOOLEAN menu_run(
|
|||||||
log_error_stall(L"Error switching console mode: %r", err);
|
log_error_stall(L"Error switching console mode: %r", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UINTN line_width = 0, entry_padding = 3;
|
||||||
while (!exit) {
|
while (!exit) {
|
||||||
UINT64 key;
|
UINT64 key;
|
||||||
|
|
||||||
if (new_mode) {
|
if (new_mode) {
|
||||||
UINTN line_width = 0, entry_padding = 3;
|
|
||||||
|
|
||||||
console_query_mode(&x_max, &y_max);
|
console_query_mode(&x_max, &y_max);
|
||||||
|
|
||||||
/* account for padding+status */
|
/* account for padding+status */
|
||||||
@ -643,6 +659,7 @@ static BOOLEAN menu_run(
|
|||||||
idx_last = idx_first + visible_max - 1;
|
idx_last = idx_first + visible_max - 1;
|
||||||
|
|
||||||
/* length of the longest entry */
|
/* length of the longest entry */
|
||||||
|
line_width = 0;
|
||||||
for (UINTN i = 0; i < config->entry_count; i++)
|
for (UINTN i = 0; i < config->entry_count; i++)
|
||||||
line_width = MAX(line_width, StrLen(config->entries[i]->title_show));
|
line_width = MAX(line_width, StrLen(config->entries[i]->title_show));
|
||||||
line_width = MIN(line_width + 2 * entry_padding, x_max);
|
line_width = MIN(line_width + 2 * entry_padding, x_max);
|
||||||
@ -655,10 +672,11 @@ static BOOLEAN menu_run(
|
|||||||
y_start = 0;
|
y_start = 0;
|
||||||
|
|
||||||
/* Put status line after the entry list, but give it some breathing room. */
|
/* Put status line after the entry list, but give it some breathing room. */
|
||||||
y_status = MIN(y_start + MIN(visible_max, config->entry_count) + 4, y_max - 1);
|
y_status = MIN(y_start + MIN(visible_max, config->entry_count) + 1, y_max - 1);
|
||||||
|
|
||||||
lines = strv_free(lines);
|
lines = strv_free(lines);
|
||||||
clearline = mfree(clearline);
|
clearline = mfree(clearline);
|
||||||
|
separator = mfree(separator);
|
||||||
|
|
||||||
/* menu entries title lines */
|
/* menu entries title lines */
|
||||||
lines = xnew(CHAR16*, config->entry_count + 1);
|
lines = xnew(CHAR16*, config->entry_count + 1);
|
||||||
@ -682,9 +700,13 @@ static BOOLEAN menu_run(
|
|||||||
lines[config->entry_count] = NULL;
|
lines[config->entry_count] = NULL;
|
||||||
|
|
||||||
clearline = xnew(CHAR16, x_max + 1);
|
clearline = xnew(CHAR16, x_max + 1);
|
||||||
for (UINTN i = 0; i < x_max; i++)
|
separator = xnew(CHAR16, x_max + 1);
|
||||||
|
for (UINTN i = 0; i < x_max; i++) {
|
||||||
clearline[i] = ' ';
|
clearline[i] = ' ';
|
||||||
|
separator[i] = unicode_supported() ? L'─' : L'-';
|
||||||
|
}
|
||||||
clearline[x_max] = 0;
|
clearline[x_max] = 0;
|
||||||
|
separator[x_max] = 0;
|
||||||
|
|
||||||
new_mode = FALSE;
|
new_mode = FALSE;
|
||||||
clear = TRUE;
|
clear = TRUE;
|
||||||
@ -704,16 +726,16 @@ static BOOLEAN menu_run(
|
|||||||
if (i == config->idx_default_efivar)
|
if (i == config->idx_default_efivar)
|
||||||
print_at(x_start, y_start + i - idx_first,
|
print_at(x_start, y_start + i - idx_first,
|
||||||
(i == idx_highlight) ? COLOR_HIGHLIGHT : COLOR_ENTRY,
|
(i == idx_highlight) ? COLOR_HIGHLIGHT : COLOR_ENTRY,
|
||||||
(CHAR16*) L"=>");
|
(CHAR16*) (unicode_supported() ? L" ►" : L"=>"));
|
||||||
}
|
}
|
||||||
refresh = FALSE;
|
refresh = FALSE;
|
||||||
} else if (highlight) {
|
} else if (highlight) {
|
||||||
print_at(x_start, y_start + idx_highlight_prev - idx_first, COLOR_ENTRY, lines[idx_highlight_prev]);
|
print_at(x_start, y_start + idx_highlight_prev - idx_first, COLOR_ENTRY, lines[idx_highlight_prev]);
|
||||||
print_at(x_start, y_start + idx_highlight - idx_first, COLOR_HIGHLIGHT, lines[idx_highlight]);
|
print_at(x_start, y_start + idx_highlight - idx_first, COLOR_HIGHLIGHT, lines[idx_highlight]);
|
||||||
if (idx_highlight_prev == config->idx_default_efivar)
|
if (idx_highlight_prev == config->idx_default_efivar)
|
||||||
print_at(x_start , y_start + idx_highlight_prev - idx_first, COLOR_ENTRY, (CHAR16*) L"=>");
|
print_at(x_start , y_start + idx_highlight_prev - idx_first, COLOR_ENTRY, (CHAR16*) (unicode_supported() ? L" ►" : L"=>"));
|
||||||
if (idx_highlight == config->idx_default_efivar)
|
if (idx_highlight == config->idx_default_efivar)
|
||||||
print_at(x_start, y_start + idx_highlight - idx_first, COLOR_HIGHLIGHT, (CHAR16*) L"=>");
|
print_at(x_start, y_start + idx_highlight - idx_first, COLOR_HIGHLIGHT, (CHAR16*) (unicode_supported() ? L" ►" : L"=>"));
|
||||||
highlight = FALSE;
|
highlight = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -722,20 +744,24 @@ static BOOLEAN menu_run(
|
|||||||
status = xpool_print(L"Boot in %u s.", timeout_remain);
|
status = xpool_print(L"Boot in %u s.", timeout_remain);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* print status at last line of screen */
|
|
||||||
if (status) {
|
if (status) {
|
||||||
UINTN len;
|
/* If we draw the last char of the last line, the screen will scroll and break our
|
||||||
UINTN x;
|
* input. Therefore, draw one less character then we could for the status message.
|
||||||
|
* Note that the same does not apply for the separator line as it will never be drawn
|
||||||
/* center line */
|
* on the last line. */
|
||||||
len = StrLen(status);
|
UINTN len = StrnLen(status, x_max - 1);
|
||||||
if (len < x_max)
|
UINTN x = (x_max - len) / 2;
|
||||||
x = (x_max - len) / 2;
|
status[len] = '\0';
|
||||||
else
|
print_at(0, y_status, COLOR_NORMAL, clearline + x_max - x);
|
||||||
x = 0;
|
|
||||||
print_at(0, y_status, COLOR_NORMAL, clearline + (x_max - x));
|
|
||||||
ST->ConOut->OutputString(ST->ConOut, status);
|
ST->ConOut->OutputString(ST->ConOut, status);
|
||||||
ST->ConOut->OutputString(ST->ConOut, clearline + 1 + x + len);
|
ST->ConOut->OutputString(ST->ConOut, clearline + 1 + x + len);
|
||||||
|
|
||||||
|
len = MIN(MAX(len, line_width) + 2 * entry_padding, x_max);
|
||||||
|
x = (x_max - len) / 2;
|
||||||
|
print_at(x, y_status - 1, COLOR_NORMAL, separator + x_max - len);
|
||||||
|
} else {
|
||||||
|
print_at(0, y_status - 1, COLOR_NORMAL, clearline);
|
||||||
|
print_at(0, y_status, COLOR_NORMAL, clearline + 1); /* See comment above. */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Beep several times so that the selected entry can be distinguished. */
|
/* Beep several times so that the selected entry can be distinguished. */
|
||||||
@ -766,11 +792,7 @@ static BOOLEAN menu_run(
|
|||||||
timeout_remain = 0;
|
timeout_remain = 0;
|
||||||
|
|
||||||
/* clear status after keystroke */
|
/* clear status after keystroke */
|
||||||
if (status) {
|
status = mfree(status);
|
||||||
FreePool(status);
|
|
||||||
status = NULL;
|
|
||||||
print_at(0, y_status, COLOR_NORMAL, clearline + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
idx_highlight_prev = idx_highlight;
|
idx_highlight_prev = idx_highlight;
|
||||||
|
|
||||||
@ -1026,6 +1048,7 @@ static void config_entry_free(ConfigEntry *entry) {
|
|||||||
FreePool(entry->id);
|
FreePool(entry->id);
|
||||||
FreePool(entry->title_show);
|
FreePool(entry->title_show);
|
||||||
FreePool(entry->title);
|
FreePool(entry->title);
|
||||||
|
FreePool(entry->sort_key);
|
||||||
FreePool(entry->version);
|
FreePool(entry->version);
|
||||||
FreePool(entry->machine_id);
|
FreePool(entry->machine_id);
|
||||||
FreePool(entry->loader);
|
FreePool(entry->loader);
|
||||||
@ -1427,6 +1450,12 @@ static void config_entry_add_from_file(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strcmpa((CHAR8 *)"sort-key", key) == 0) {
|
||||||
|
FreePool(entry->sort_key);
|
||||||
|
entry->sort_key = xstra_to_str(value);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (strcmpa((CHAR8 *)"version", key) == 0) {
|
if (strcmpa((CHAR8 *)"version", key) == 0) {
|
||||||
FreePool(entry->version);
|
FreePool(entry->version);
|
||||||
entry->version = xstra_to_str(value);
|
entry->version = xstra_to_str(value);
|
||||||
@ -1531,6 +1560,7 @@ static void config_entry_add_from_file(
|
|||||||
|
|
||||||
entry->device = device;
|
entry->device = device;
|
||||||
entry->id = xstrdup(file);
|
entry->id = xstrdup(file);
|
||||||
|
StrLwr(entry->id);
|
||||||
|
|
||||||
config_add_entry(config, entry);
|
config_add_entry(config, entry);
|
||||||
|
|
||||||
@ -1609,6 +1639,8 @@ static void config_load_entries(
|
|||||||
assert(device);
|
assert(device);
|
||||||
assert(root_dir);
|
assert(root_dir);
|
||||||
|
|
||||||
|
/* Adds Boot Loader Type #1 entries (i.e. /loader/entries/….conf) */
|
||||||
|
|
||||||
err = open_directory(root_dir, L"\\loader\\entries", &entries_dir);
|
err = open_directory(root_dir, L"\\loader\\entries", &entries_dir);
|
||||||
if (EFI_ERROR(err))
|
if (EFI_ERROR(err))
|
||||||
return;
|
return;
|
||||||
@ -1642,24 +1674,40 @@ static INTN config_entry_compare(const ConfigEntry *a, const ConfigEntry *b) {
|
|||||||
assert(a);
|
assert(a);
|
||||||
assert(b);
|
assert(b);
|
||||||
|
|
||||||
/* Order entries that have no tries left towards the end of the list. They have
|
/* Order entries that have no tries left to the end of the list */
|
||||||
* proven to be bad and should not be selected automatically. */
|
|
||||||
if (a->tries_left != 0 && b->tries_left == 0)
|
|
||||||
return -1;
|
|
||||||
if (a->tries_left == 0 && b->tries_left != 0)
|
if (a->tries_left == 0 && b->tries_left != 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
if (a->tries_left != 0 && b->tries_left == 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
r = strcasecmp_ptr(a->title ?: a->id, b->title ?: b->id);
|
/* If there's a sort key defined for *both* entries, then we do new-style ordering, i.e. by
|
||||||
if (r != 0)
|
* sort-key/machine-id/version, with a final fallback to id. If there's no sort key for either, we do
|
||||||
|
* old-style ordering, i.e. by id only. If one has sort key and the other does not, we put new-style
|
||||||
|
* before old-style. */
|
||||||
|
r = CMP(!a->sort_key, !b->sort_key);
|
||||||
|
if (r != 0) /* one is old-style, one new-style */
|
||||||
return r;
|
return r;
|
||||||
|
if (a->sort_key && b->sort_key) {
|
||||||
|
|
||||||
/* Sort by machine id now so that different installations don't interleave their versions. */
|
r = strcmp(a->sort_key, b->sort_key);
|
||||||
r = strcasecmp_ptr(a->machine_id, b->machine_id);
|
if (r != 0)
|
||||||
if (r != 0)
|
return r;
|
||||||
return r;
|
|
||||||
|
|
||||||
/* Reverse version comparison order so that higher versions are preferred. */
|
/* If multiple installations of the same OS are around, group by machine ID */
|
||||||
r = strverscmp_improved(b->version, a->version);
|
r = strcmp_ptr(a->machine_id, b->machine_id);
|
||||||
|
if (r != 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
/* If the sort key was defined, then order by version now (downwards, putting the newest first) */
|
||||||
|
r = -strverscmp_improved(a->version, b->version);
|
||||||
|
if (r != 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now order by ID (the version is likely part of the ID, thus note that this might put the oldest
|
||||||
|
* version last, not first, i.e. specifying a sort key explicitly is thus generally preferable, to
|
||||||
|
* take benefit of the explicit sorting above.) */
|
||||||
|
r = strverscmp_improved(a->id, b->id);
|
||||||
if (r != 0)
|
if (r != 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -1668,19 +1716,18 @@ static INTN config_entry_compare(const ConfigEntry *a, const ConfigEntry *b) {
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* If both items have boot counting, and otherwise are identical, put the entry with more tries left first */
|
/* If both items have boot counting, and otherwise are identical, put the entry with more tries left first */
|
||||||
if (a->tries_left > b->tries_left)
|
|
||||||
return -1;
|
|
||||||
if (a->tries_left < b->tries_left)
|
if (a->tries_left < b->tries_left)
|
||||||
return 1;
|
return 1;
|
||||||
|
if (a->tries_left > b->tries_left)
|
||||||
|
return -1;
|
||||||
|
|
||||||
/* If they have the same number of tries left, then let the one win which was tried fewer times so far */
|
/* If they have the same number of tries left, then let the one win which was tried fewer times so far */
|
||||||
if (a->tries_done < b->tries_done)
|
|
||||||
return -1;
|
|
||||||
if (a->tries_done > b->tries_done)
|
if (a->tries_done > b->tries_done)
|
||||||
return 1;
|
return 1;
|
||||||
|
if (a->tries_done < b->tries_done)
|
||||||
|
return -1;
|
||||||
|
|
||||||
/* As a last resort, use the id (file name). */
|
return 0;
|
||||||
return strverscmp_improved(a->id, b->id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static UINTN config_entry_find(Config *config, const CHAR16 *needle) {
|
static UINTN config_entry_find(Config *config, const CHAR16 *needle) {
|
||||||
@ -1724,7 +1771,7 @@ static void config_default_entry_select(Config *config) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Select the first suitable entry. */
|
/* select the first suitable entry */
|
||||||
for (i = 0; i < config->entry_count; i++) {
|
for (i = 0; i < config->entry_count; i++) {
|
||||||
if (config->entries[i]->type == LOADER_AUTO || config->entries[i]->call)
|
if (config->entries[i]->type == LOADER_AUTO || config->entries[i]->call)
|
||||||
continue;
|
continue;
|
||||||
@ -1853,6 +1900,7 @@ static ConfigEntry *config_entry_add_loader(
|
|||||||
CHAR16 key,
|
CHAR16 key,
|
||||||
const CHAR16 *title,
|
const CHAR16 *title,
|
||||||
const CHAR16 *loader,
|
const CHAR16 *loader,
|
||||||
|
const CHAR16 *sort_key,
|
||||||
const CHAR16 *version) {
|
const CHAR16 *version) {
|
||||||
|
|
||||||
ConfigEntry *entry;
|
ConfigEntry *entry;
|
||||||
@ -1871,11 +1919,14 @@ static ConfigEntry *config_entry_add_loader(
|
|||||||
.device = device,
|
.device = device,
|
||||||
.loader = xstrdup(loader),
|
.loader = xstrdup(loader),
|
||||||
.id = xstrdup(id),
|
.id = xstrdup(id),
|
||||||
|
.sort_key = xstrdup(sort_key),
|
||||||
.key = key,
|
.key = key,
|
||||||
.tries_done = UINTN_MAX,
|
.tries_done = UINTN_MAX,
|
||||||
.tries_left = UINTN_MAX,
|
.tries_left = UINTN_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
StrLwr(entry->id);
|
||||||
|
|
||||||
config_add_entry(config, entry);
|
config_add_entry(config, entry);
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
@ -1943,7 +1994,7 @@ static ConfigEntry *config_entry_add_loader_auto(
|
|||||||
if (EFI_ERROR(err))
|
if (EFI_ERROR(err))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
return config_entry_add_loader(config, device, LOADER_AUTO, id, key, title, loader, NULL);
|
return config_entry_add_loader(config, device, LOADER_AUTO, id, key, title, loader, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void config_entry_add_osx(Config *config) {
|
static void config_entry_add_osx(Config *config) {
|
||||||
@ -2097,6 +2148,8 @@ static void config_entry_add_linux(
|
|||||||
UINTN f_size = 0;
|
UINTN f_size = 0;
|
||||||
EFI_STATUS err;
|
EFI_STATUS err;
|
||||||
|
|
||||||
|
/* Adds Boot Loader Type #2 entries (i.e. /EFI/Linux/….efi) */
|
||||||
|
|
||||||
assert(config);
|
assert(config);
|
||||||
assert(device);
|
assert(device);
|
||||||
assert(root_dir);
|
assert(root_dir);
|
||||||
@ -2121,7 +2174,7 @@ static void config_entry_add_linux(
|
|||||||
_cleanup_freepool_ CHAR16 *os_pretty_name = NULL, *os_image_id = NULL, *os_name = NULL, *os_id = NULL,
|
_cleanup_freepool_ CHAR16 *os_pretty_name = NULL, *os_image_id = NULL, *os_name = NULL, *os_id = NULL,
|
||||||
*os_image_version = NULL, *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL,
|
*os_image_version = NULL, *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL,
|
||||||
*path = NULL;
|
*path = NULL;
|
||||||
const CHAR16 *good_name, *good_version;
|
const CHAR16 *good_name, *good_version, *good_sort_key;
|
||||||
_cleanup_freepool_ CHAR8 *content = NULL;
|
_cleanup_freepool_ CHAR8 *content = NULL;
|
||||||
UINTN offs[_SECTION_MAX] = {};
|
UINTN offs[_SECTION_MAX] = {};
|
||||||
UINTN szs[_SECTION_MAX] = {};
|
UINTN szs[_SECTION_MAX] = {};
|
||||||
@ -2202,7 +2255,7 @@ static void config_entry_add_linux(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bootspec_pick_name_version(
|
if (!bootspec_pick_name_version_sort_key(
|
||||||
os_pretty_name,
|
os_pretty_name,
|
||||||
os_image_id,
|
os_image_id,
|
||||||
os_name,
|
os_name,
|
||||||
@ -2212,7 +2265,8 @@ static void config_entry_add_linux(
|
|||||||
os_version_id,
|
os_version_id,
|
||||||
os_build_id,
|
os_build_id,
|
||||||
&good_name,
|
&good_name,
|
||||||
&good_version))
|
&good_version,
|
||||||
|
&good_sort_key))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
path = xpool_print(L"\\EFI\\Linux\\%s", f->FileName);
|
path = xpool_print(L"\\EFI\\Linux\\%s", f->FileName);
|
||||||
@ -2220,10 +2274,11 @@ static void config_entry_add_linux(
|
|||||||
config,
|
config,
|
||||||
device,
|
device,
|
||||||
LOADER_UNIFIED_LINUX,
|
LOADER_UNIFIED_LINUX,
|
||||||
f->FileName,
|
/* id= */ f->FileName,
|
||||||
/* key= */ 'l',
|
/* key= */ 'l',
|
||||||
good_name,
|
/* title= */ good_name,
|
||||||
path,
|
/* loader= */ path,
|
||||||
|
/* sort_key= */ good_sort_key,
|
||||||
good_version);
|
good_version);
|
||||||
|
|
||||||
config_entry_parse_tries(entry, L"\\EFI\\Linux", f->FileName, L".efi");
|
config_entry_parse_tries(entry, L"\\EFI\\Linux", f->FileName, L".efi");
|
||||||
@ -2449,14 +2504,12 @@ static void config_load_all_entries(
|
|||||||
/* Similar, but on any XBOOTLDR partition */
|
/* Similar, but on any XBOOTLDR partition */
|
||||||
config_load_xbootldr(config, loaded_image->DeviceHandle);
|
config_load_xbootldr(config, loaded_image->DeviceHandle);
|
||||||
|
|
||||||
/* Add these now, so they get sorted with the rest. */
|
|
||||||
config_entry_add_osx(config);
|
|
||||||
config_entry_add_windows(config, loaded_image->DeviceHandle, root_dir);
|
|
||||||
|
|
||||||
/* sort entries after version number */
|
/* sort entries after version number */
|
||||||
sort_pointer_array((void **) config->entries, config->entry_count, (compare_pointer_func_t) config_entry_compare);
|
sort_pointer_array((void **) config->entries, config->entry_count, (compare_pointer_func_t) config_entry_compare);
|
||||||
|
|
||||||
/* if we find some well-known loaders, add them to the end of the list */
|
/* if we find some well-known loaders, add them to the end of the list */
|
||||||
|
config_entry_add_osx(config);
|
||||||
|
config_entry_add_windows(config, loaded_image->DeviceHandle, root_dir);
|
||||||
config_entry_add_loader_auto(config, loaded_image->DeviceHandle, root_dir, NULL,
|
config_entry_add_loader_auto(config, loaded_image->DeviceHandle, root_dir, NULL,
|
||||||
L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" EFI_MACHINE_TYPE_NAME ".efi");
|
L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" EFI_MACHINE_TYPE_NAME ".efi");
|
||||||
config_entry_add_loader_auto(config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
|
config_entry_add_loader_auto(config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
|
||||||
|
|||||||
@ -1571,8 +1571,6 @@ static void initialize_clock(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void apply_clock_update(void) {
|
static void apply_clock_update(void) {
|
||||||
struct timespec ts;
|
|
||||||
|
|
||||||
/* This is called later than initialize_clock(), i.e. after we parsed configuration files/kernel
|
/* This is called later than initialize_clock(), i.e. after we parsed configuration files/kernel
|
||||||
* command line and such. */
|
* command line and such. */
|
||||||
|
|
||||||
@ -1582,7 +1580,7 @@ static void apply_clock_update(void) {
|
|||||||
if (getpid_cached() != 1)
|
if (getpid_cached() != 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, arg_clock_usec)) < 0)
|
if (clock_settime(CLOCK_REALTIME, TIMESPEC_STORE(arg_clock_usec)) < 0)
|
||||||
log_error_errno(errno, "Failed to set system clock to time specified on kernel command line: %m");
|
log_error_errno(errno, "Failed to set system clock to time specified on kernel command line: %m");
|
||||||
else
|
else
|
||||||
log_info("Set system clock to %s, as specified on the kernel command line.",
|
log_info("Set system clock to %s, as specified on the kernel command line.",
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include "bootspec-fundamental.h"
|
#include "bootspec-fundamental.h"
|
||||||
|
|
||||||
sd_bool bootspec_pick_name_version(
|
sd_bool bootspec_pick_name_version_sort_key(
|
||||||
const sd_char *os_pretty_name,
|
const sd_char *os_pretty_name,
|
||||||
const sd_char *os_image_id,
|
const sd_char *os_image_id,
|
||||||
const sd_char *os_name,
|
const sd_char *os_name,
|
||||||
@ -12,12 +12,14 @@ sd_bool bootspec_pick_name_version(
|
|||||||
const sd_char *os_version_id,
|
const sd_char *os_version_id,
|
||||||
const sd_char *os_build_id,
|
const sd_char *os_build_id,
|
||||||
const sd_char **ret_name,
|
const sd_char **ret_name,
|
||||||
const sd_char **ret_version) {
|
const sd_char **ret_version,
|
||||||
|
const sd_char **ret_sort_key) {
|
||||||
|
|
||||||
const sd_char *good_name, *good_version;
|
const sd_char *good_name, *good_version, *good_sort_key;
|
||||||
|
|
||||||
/* Find the best human readable title and version string for a boot entry (using the os-release(5)
|
/* Find the best human readable title, version string and sort key for a boot entry (using the
|
||||||
* fields). Precise is preferred over vague, and human readable over machine readable. Thus:
|
* os-release(5) fields). Precise is preferred over vague, and human readable over machine
|
||||||
|
* readable. Thus:
|
||||||
*
|
*
|
||||||
* 1. First priority gets the PRETTY_NAME field, which is the primary string intended for display,
|
* 1. First priority gets the PRETTY_NAME field, which is the primary string intended for display,
|
||||||
* and should already contain both a nice description and a version indication (if that concept
|
* and should already contain both a nice description and a version indication (if that concept
|
||||||
@ -37,11 +39,12 @@ sd_bool bootspec_pick_name_version(
|
|||||||
* which case the version is shown too.
|
* which case the version is shown too.
|
||||||
*
|
*
|
||||||
* Note that name/version determined here are used only for display purposes. Boot entry preference
|
* Note that name/version determined here are used only for display purposes. Boot entry preference
|
||||||
* sorting (i.e. algorithmic ordering of boot entries) is done based on the order of the entry "id"
|
* sorting (i.e. algorithmic ordering of boot entries) is done based on the order of the sort key (if
|
||||||
* string (i.e. not on os-release(5) data). */
|
* defined) or entry "id" string (i.e. entry file name) otherwise. */
|
||||||
|
|
||||||
good_name = os_pretty_name ?: (os_image_id ?: (os_name ?: os_id));
|
good_name = os_pretty_name ?: (os_image_id ?: (os_name ?: os_id));
|
||||||
good_version = os_image_version ?: (os_version ?: (os_version_id ? : os_build_id));
|
good_version = os_image_version ?: (os_version ?: (os_version_id ? : os_build_id));
|
||||||
|
good_sort_key = os_image_id ?: os_id;
|
||||||
|
|
||||||
if (!good_name || !good_version)
|
if (!good_name || !good_version)
|
||||||
return sd_false;
|
return sd_false;
|
||||||
@ -52,5 +55,8 @@ sd_bool bootspec_pick_name_version(
|
|||||||
if (ret_version)
|
if (ret_version)
|
||||||
*ret_version = good_version;
|
*ret_version = good_version;
|
||||||
|
|
||||||
|
if (ret_sort_key)
|
||||||
|
*ret_sort_key = good_sort_key;
|
||||||
|
|
||||||
return sd_true;
|
return sd_true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include "types-fundamental.h"
|
#include "types-fundamental.h"
|
||||||
|
|
||||||
sd_bool bootspec_pick_name_version(
|
sd_bool bootspec_pick_name_version_sort_key(
|
||||||
const sd_char *os_pretty_name,
|
const sd_char *os_pretty_name,
|
||||||
const sd_char *os_image_id,
|
const sd_char *os_image_id,
|
||||||
const sd_char *os_name,
|
const sd_char *os_name,
|
||||||
@ -13,4 +13,5 @@ sd_bool bootspec_pick_name_version(
|
|||||||
const sd_char *os_version_id,
|
const sd_char *os_version_id,
|
||||||
const sd_char *os_build_id,
|
const sd_char *os_build_id,
|
||||||
const sd_char **ret_name,
|
const sd_char **ret_name,
|
||||||
const sd_char **ret_version);
|
const sd_char **ret_version,
|
||||||
|
const sd_char **ret_sort_key);
|
||||||
|
|||||||
@ -62,6 +62,9 @@ fi
|
|||||||
|
|
||||||
[ -n "$PRETTY_NAME" ] || PRETTY_NAME="Linux $KERNEL_VERSION"
|
[ -n "$PRETTY_NAME" ] || PRETTY_NAME="Linux $KERNEL_VERSION"
|
||||||
|
|
||||||
|
SORT_KEY="$IMAGE_ID"
|
||||||
|
[ -z "$SORT_KEY" ] && SORT_KEY="$ID"
|
||||||
|
|
||||||
if [ -r /etc/kernel/cmdline ]; then
|
if [ -r /etc/kernel/cmdline ]; then
|
||||||
BOOT_OPTIONS="$(tr -s "$IFS" ' ' </etc/kernel/cmdline)"
|
BOOT_OPTIONS="$(tr -s "$IFS" ' ' </etc/kernel/cmdline)"
|
||||||
elif [ -r /usr/lib/kernel/cmdline ]; then
|
elif [ -r /usr/lib/kernel/cmdline ]; then
|
||||||
@ -130,6 +133,7 @@ mkdir -p "${LOADER_ENTRY%/*}" || {
|
|||||||
# See similar logic above for the systemd.machine_id= kernel command line option
|
# See similar logic above for the systemd.machine_id= kernel command line option
|
||||||
echo "machine-id $MACHINE_ID"
|
echo "machine-id $MACHINE_ID"
|
||||||
fi
|
fi
|
||||||
|
[ -n "$SORT_KEY" ] && echo "sort-key $SORT_KEY"
|
||||||
echo "options $BOOT_OPTIONS"
|
echo "options $BOOT_OPTIONS"
|
||||||
echo "linux $ENTRY_DIR/linux"
|
echo "linux $ENTRY_DIR/linux"
|
||||||
|
|
||||||
|
|||||||
@ -3906,6 +3906,7 @@ static int epoll_wait_usec(
|
|||||||
int msec;
|
int msec;
|
||||||
#if 0
|
#if 0
|
||||||
static bool epoll_pwait2_absent = false;
|
static bool epoll_pwait2_absent = false;
|
||||||
|
int r;
|
||||||
|
|
||||||
/* A wrapper that uses epoll_pwait2() if available, and falls back to epoll_wait() if not.
|
/* A wrapper that uses epoll_pwait2() if available, and falls back to epoll_wait() if not.
|
||||||
*
|
*
|
||||||
@ -3914,12 +3915,10 @@ static int epoll_wait_usec(
|
|||||||
* https://github.com/systemd/systemd/issues/19052. */
|
* https://github.com/systemd/systemd/issues/19052. */
|
||||||
|
|
||||||
if (!epoll_pwait2_absent && timeout != USEC_INFINITY) {
|
if (!epoll_pwait2_absent && timeout != USEC_INFINITY) {
|
||||||
struct timespec ts;
|
|
||||||
|
|
||||||
r = epoll_pwait2(fd,
|
r = epoll_pwait2(fd,
|
||||||
events,
|
events,
|
||||||
maxevents,
|
maxevents,
|
||||||
timespec_store(&ts, timeout),
|
TIMESPEC_STORE(timeout),
|
||||||
NULL);
|
NULL);
|
||||||
if (r >= 0)
|
if (r >= 0)
|
||||||
return r;
|
return r;
|
||||||
|
|||||||
@ -142,10 +142,8 @@ int stub_pid1(sd_id128_t uuid) {
|
|||||||
|
|
||||||
if (quit_usec == USEC_INFINITY)
|
if (quit_usec == USEC_INFINITY)
|
||||||
r = sigwaitinfo(&waitmask, &si);
|
r = sigwaitinfo(&waitmask, &si);
|
||||||
else {
|
else
|
||||||
struct timespec ts;
|
r = sigtimedwait(&waitmask, &si, TIMESPEC_STORE(quit_usec - current_usec));
|
||||||
r = sigtimedwait(&waitmask, &si, timespec_store(&ts, quit_usec - current_usec));
|
|
||||||
}
|
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
if (errno == EINTR) /* strace -p attach can result in EINTR, let's handle this nicely. */
|
if (errno == EINTR) /* strace -p attach can result in EINTR, let's handle this nicely. */
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@ -1,40 +1,22 @@
|
|||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <linux/magic.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "sd-device.h"
|
|
||||||
#include "sd-id128.h"
|
|
||||||
|
|
||||||
#include "alloc-util.h"
|
|
||||||
#include "blkid-util.h"
|
|
||||||
#include "bootspec-fundamental.h"
|
|
||||||
#include "bootspec.h"
|
#include "bootspec.h"
|
||||||
|
#include "bootspec-fundamental.h"
|
||||||
#include "conf-files.h"
|
#include "conf-files.h"
|
||||||
#include "def.h"
|
|
||||||
#include "device-nodes.h"
|
|
||||||
#include "dirent-util.h"
|
#include "dirent-util.h"
|
||||||
#include "efivars.h"
|
|
||||||
#include "efi-loader.h"
|
#include "efi-loader.h"
|
||||||
#include "env-file.h"
|
#include "env-file.h"
|
||||||
#include "env-util.h"
|
|
||||||
#include "errno-util.h"
|
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "fileio.h"
|
#include "fileio.h"
|
||||||
#include "gpt.h"
|
#include "find-esp.h"
|
||||||
#include "id128-util.h"
|
|
||||||
#include "parse-util.h"
|
|
||||||
#include "path-util.h"
|
#include "path-util.h"
|
||||||
#include "pe-header.h"
|
#include "pe-header.h"
|
||||||
#include "sort-util.h"
|
#include "sort-util.h"
|
||||||
#include "stat-util.h"
|
#include "stat-util.h"
|
||||||
#include "string-table.h"
|
|
||||||
#include "string-util.h"
|
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
#include "unaligned.h"
|
#include "unaligned.h"
|
||||||
#include "util.h"
|
|
||||||
#include "virt.h"
|
|
||||||
|
|
||||||
static void boot_entry_free(BootEntry *entry) {
|
static void boot_entry_free(BootEntry *entry) {
|
||||||
assert(entry);
|
assert(entry);
|
||||||
@ -45,6 +27,7 @@ static void boot_entry_free(BootEntry *entry) {
|
|||||||
free(entry->root);
|
free(entry->root);
|
||||||
free(entry->title);
|
free(entry->title);
|
||||||
free(entry->show_title);
|
free(entry->show_title);
|
||||||
|
free(entry->sort_key);
|
||||||
free(entry->version);
|
free(entry->version);
|
||||||
free(entry->machine_id);
|
free(entry->machine_id);
|
||||||
free(entry->architecture);
|
free(entry->architecture);
|
||||||
@ -131,6 +114,8 @@ static int boot_entry_load(
|
|||||||
|
|
||||||
if (streq(field, "title"))
|
if (streq(field, "title"))
|
||||||
r = free_and_strdup(&tmp.title, p);
|
r = free_and_strdup(&tmp.title, p);
|
||||||
|
else if (streq(field, "sort-key"))
|
||||||
|
r = free_and_strdup(&tmp.sort_key, p);
|
||||||
else if (streq(field, "version"))
|
else if (streq(field, "version"))
|
||||||
r = free_and_strdup(&tmp.version, p);
|
r = free_and_strdup(&tmp.version, p);
|
||||||
else if (streq(field, "machine-id"))
|
else if (streq(field, "machine-id"))
|
||||||
@ -263,6 +248,28 @@ static int boot_loader_read_conf(const char *path, BootConfig *config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int boot_entry_compare(const BootEntry *a, const BootEntry *b) {
|
static int boot_entry_compare(const BootEntry *a, const BootEntry *b) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(a);
|
||||||
|
assert(b);
|
||||||
|
|
||||||
|
r = CMP(!a->sort_key, !b->sort_key);
|
||||||
|
if (r != 0)
|
||||||
|
return r;
|
||||||
|
if (a->sort_key && b->sort_key) {
|
||||||
|
r = strcmp(a->sort_key, b->sort_key);
|
||||||
|
if (r != 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = strcmp_ptr(a->machine_id, b->machine_id);
|
||||||
|
if (r != 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = -strverscmp_improved(a->version, b->version);
|
||||||
|
if (r != 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
return strverscmp_improved(a->id, b->id);
|
return strverscmp_improved(a->id, b->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,7 +318,7 @@ static int boot_entry_load_unified(
|
|||||||
_cleanup_(boot_entry_free) BootEntry tmp = {
|
_cleanup_(boot_entry_free) BootEntry tmp = {
|
||||||
.type = BOOT_ENTRY_UNIFIED,
|
.type = BOOT_ENTRY_UNIFIED,
|
||||||
};
|
};
|
||||||
const char *k, *good_name, *good_version;
|
const char *k, *good_name, *good_version, *good_sort_key;
|
||||||
_cleanup_fclose_ FILE *f = NULL;
|
_cleanup_fclose_ FILE *f = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -339,7 +346,7 @@ static int boot_entry_load_unified(
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to parse os-release data from unified kernel image %s: %m", path);
|
return log_error_errno(r, "Failed to parse os-release data from unified kernel image %s: %m", path);
|
||||||
|
|
||||||
if (!bootspec_pick_name_version(
|
if (!bootspec_pick_name_version_sort_key(
|
||||||
os_pretty_name,
|
os_pretty_name,
|
||||||
os_image_id,
|
os_image_id,
|
||||||
os_name,
|
os_name,
|
||||||
@ -349,7 +356,8 @@ static int boot_entry_load_unified(
|
|||||||
os_version_id,
|
os_version_id,
|
||||||
os_build_id,
|
os_build_id,
|
||||||
&good_name,
|
&good_name,
|
||||||
&good_version))
|
&good_version,
|
||||||
|
&good_sort_key))
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Missing fields in os-release data from unified kernel image %s, refusing.", path);
|
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Missing fields in os-release data from unified kernel image %s, refusing.", path);
|
||||||
|
|
||||||
r = path_extract_filename(path, &tmp.id);
|
r = path_extract_filename(path, &tmp.id);
|
||||||
@ -387,6 +395,10 @@ static int boot_entry_load_unified(
|
|||||||
if (!tmp.title)
|
if (!tmp.title)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
|
tmp.sort_key = strdup(good_sort_key);
|
||||||
|
if (!tmp.sort_key)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
tmp.version = strdup(good_version);
|
tmp.version = strdup(good_version);
|
||||||
if (!tmp.version)
|
if (!tmp.version)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
@ -634,6 +646,19 @@ static int boot_entries_uniquify(BootEntry *entries, size_t n_entries) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int boot_config_find(const BootConfig *config, const char *id) {
|
||||||
|
assert(config);
|
||||||
|
|
||||||
|
if (!id)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < config->n_entries; i++)
|
||||||
|
if (fnmatch(id, config->entries[i].id, FNM_CASEFOLD) == 0)
|
||||||
|
return i;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static int boot_entries_select_default(const BootConfig *config) {
|
static int boot_entries_select_default(const BootConfig *config) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -645,47 +670,45 @@ static int boot_entries_select_default(const BootConfig *config) {
|
|||||||
return -1; /* -1 means "no default" */
|
return -1; /* -1 means "no default" */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config->entry_oneshot)
|
if (config->entry_oneshot) {
|
||||||
for (i = config->n_entries - 1; i >= 0; i--)
|
i = boot_config_find(config, config->entry_oneshot);
|
||||||
if (streq(config->entry_oneshot, config->entries[i].id)) {
|
if (i >= 0) {
|
||||||
log_debug("Found default: id \"%s\" is matched by LoaderEntryOneShot",
|
log_debug("Found default: id \"%s\" is matched by LoaderEntryOneShot",
|
||||||
config->entries[i].id);
|
config->entries[i].id);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (config->entry_default)
|
if (config->entry_default) {
|
||||||
for (i = config->n_entries - 1; i >= 0; i--)
|
i = boot_config_find(config, config->entry_default);
|
||||||
if (streq(config->entry_default, config->entries[i].id)) {
|
if (i >= 0) {
|
||||||
log_debug("Found default: id \"%s\" is matched by LoaderEntryDefault",
|
log_debug("Found default: id \"%s\" is matched by LoaderEntryDefault",
|
||||||
config->entries[i].id);
|
config->entries[i].id);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (config->default_pattern)
|
if (config->default_pattern) {
|
||||||
for (i = config->n_entries - 1; i >= 0; i--)
|
i = boot_config_find(config, config->default_pattern);
|
||||||
if (fnmatch(config->default_pattern, config->entries[i].id, FNM_CASEFOLD) == 0) {
|
if (i >= 0) {
|
||||||
log_debug("Found default: id \"%s\" is matched by pattern \"%s\"",
|
log_debug("Found default: id \"%s\" is matched by pattern \"%s\"",
|
||||||
config->entries[i].id, config->default_pattern);
|
config->entries[i].id, config->default_pattern);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log_debug("Found default: last entry \"%s\"", config->entries[config->n_entries - 1].id);
|
log_debug("Found default: first entry \"%s\"", config->entries[0].id);
|
||||||
return config->n_entries - 1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int boot_entries_select_selected(const BootConfig *config) {
|
static int boot_entries_select_selected(const BootConfig *config) {
|
||||||
|
|
||||||
assert(config);
|
assert(config);
|
||||||
assert(config->entries || config->n_entries == 0);
|
assert(config->entries || config->n_entries == 0);
|
||||||
|
|
||||||
if (!config->entry_selected || config->n_entries == 0)
|
if (!config->entry_selected || config->n_entries == 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
for (int i = config->n_entries - 1; i >= 0; i--)
|
return boot_config_find(config, config->entry_selected);
|
||||||
if (streq(config->entry_selected, config->entries[i].id))
|
|
||||||
return i;
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int boot_load_efi_entry_pointers(BootConfig *config) {
|
static int boot_load_efi_entry_pointers(BootConfig *config) {
|
||||||
@ -884,696 +907,3 @@ int boot_entries_augment_from_loader(
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/********************************************************************************/
|
|
||||||
|
|
||||||
static int verify_esp_blkid(
|
|
||||||
dev_t devid,
|
|
||||||
bool searching,
|
|
||||||
uint32_t *ret_part,
|
|
||||||
uint64_t *ret_pstart,
|
|
||||||
uint64_t *ret_psize,
|
|
||||||
sd_id128_t *ret_uuid) {
|
|
||||||
|
|
||||||
sd_id128_t uuid = SD_ID128_NULL;
|
|
||||||
uint64_t pstart = 0, psize = 0;
|
|
||||||
uint32_t part = 0;
|
|
||||||
|
|
||||||
#if HAVE_BLKID
|
|
||||||
_cleanup_(blkid_free_probep) blkid_probe b = NULL;
|
|
||||||
_cleanup_free_ char *node = NULL;
|
|
||||||
const char *v;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
r = device_path_make_major_minor(S_IFBLK, devid, &node);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to format major/minor device path: %m");
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
b = blkid_new_probe_from_filename(node);
|
|
||||||
if (!b)
|
|
||||||
return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
|
|
||||||
|
|
||||||
blkid_probe_enable_superblocks(b, 1);
|
|
||||||
blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
|
|
||||||
blkid_probe_enable_partitions(b, 1);
|
|
||||||
blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
r = blkid_do_safeprobe(b);
|
|
||||||
if (r == -2)
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node);
|
|
||||||
else if (r == 1)
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
|
|
||||||
else if (r != 0)
|
|
||||||
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
|
|
||||||
|
|
||||||
r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
|
|
||||||
if (r != 0)
|
|
||||||
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
|
||||||
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
|
|
||||||
"No filesystem found on \"%s\": %m", node);
|
|
||||||
if (!streq(v, "vfat"))
|
|
||||||
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
|
||||||
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
|
|
||||||
"File system \"%s\" is not FAT.", node);
|
|
||||||
|
|
||||||
r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
|
|
||||||
if (r != 0)
|
|
||||||
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
|
||||||
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
|
|
||||||
"File system \"%s\" is not located on a partitioned block device.", node);
|
|
||||||
if (!streq(v, "gpt"))
|
|
||||||
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
|
||||||
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
|
|
||||||
"File system \"%s\" is not on a GPT partition table.", node);
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
|
|
||||||
if (r != 0)
|
|
||||||
return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID of \"%s\": %m", node);
|
|
||||||
if (id128_equal_string(v, GPT_ESP) <= 0)
|
|
||||||
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
|
||||||
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
|
|
||||||
"File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
|
|
||||||
if (r != 0)
|
|
||||||
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
|
|
||||||
r = sd_id128_from_string(v, &uuid);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
|
|
||||||
if (r != 0)
|
|
||||||
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition number of \"%s\": %m", node);
|
|
||||||
r = safe_atou32(v, &part);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL);
|
|
||||||
if (r != 0)
|
|
||||||
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition offset of \"%s\": %m", node);
|
|
||||||
r = safe_atou64(v, &pstart);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL);
|
|
||||||
if (r != 0)
|
|
||||||
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition size of \"%s\": %m", node);
|
|
||||||
r = safe_atou64(v, &psize);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ret_part)
|
|
||||||
*ret_part = part;
|
|
||||||
if (ret_pstart)
|
|
||||||
*ret_pstart = pstart;
|
|
||||||
if (ret_psize)
|
|
||||||
*ret_psize = psize;
|
|
||||||
if (ret_uuid)
|
|
||||||
*ret_uuid = uuid;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int verify_esp_udev(
|
|
||||||
dev_t devid,
|
|
||||||
bool searching,
|
|
||||||
uint32_t *ret_part,
|
|
||||||
uint64_t *ret_pstart,
|
|
||||||
uint64_t *ret_psize,
|
|
||||||
sd_id128_t *ret_uuid) {
|
|
||||||
|
|
||||||
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
|
|
||||||
_cleanup_free_ char *node = NULL;
|
|
||||||
sd_id128_t uuid = SD_ID128_NULL;
|
|
||||||
uint64_t pstart = 0, psize = 0;
|
|
||||||
uint32_t part = 0;
|
|
||||||
const char *v;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
r = device_path_make_major_minor(S_IFBLK, devid, &node);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to format major/minor device path: %m");
|
|
||||||
|
|
||||||
r = sd_device_new_from_devnum(&d, 'b', devid);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to get device from device number: %m");
|
|
||||||
|
|
||||||
r = sd_device_get_property_value(d, "ID_FS_TYPE", &v);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to get device property: %m");
|
|
||||||
if (!streq(v, "vfat"))
|
|
||||||
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
|
||||||
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
|
|
||||||
"File system \"%s\" is not FAT.", node );
|
|
||||||
|
|
||||||
r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to get device property: %m");
|
|
||||||
if (!streq(v, "gpt"))
|
|
||||||
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
|
||||||
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
|
|
||||||
"File system \"%s\" is not on a GPT partition table.", node);
|
|
||||||
|
|
||||||
r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to get device property: %m");
|
|
||||||
if (id128_equal_string(v, GPT_ESP) <= 0)
|
|
||||||
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
|
||||||
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
|
|
||||||
"File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
|
|
||||||
|
|
||||||
r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to get device property: %m");
|
|
||||||
r = sd_id128_from_string(v, &uuid);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
|
|
||||||
|
|
||||||
r = sd_device_get_property_value(d, "ID_PART_ENTRY_NUMBER", &v);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to get device property: %m");
|
|
||||||
r = safe_atou32(v, &part);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
|
|
||||||
|
|
||||||
r = sd_device_get_property_value(d, "ID_PART_ENTRY_OFFSET", &v);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to get device property: %m");
|
|
||||||
r = safe_atou64(v, &pstart);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
|
|
||||||
|
|
||||||
r = sd_device_get_property_value(d, "ID_PART_ENTRY_SIZE", &v);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to get device property: %m");
|
|
||||||
r = safe_atou64(v, &psize);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
|
|
||||||
|
|
||||||
if (ret_part)
|
|
||||||
*ret_part = part;
|
|
||||||
if (ret_pstart)
|
|
||||||
*ret_pstart = pstart;
|
|
||||||
if (ret_psize)
|
|
||||||
*ret_psize = psize;
|
|
||||||
if (ret_uuid)
|
|
||||||
*ret_uuid = uuid;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int verify_fsroot_dir(
|
|
||||||
const char *path,
|
|
||||||
bool searching,
|
|
||||||
bool unprivileged_mode,
|
|
||||||
dev_t *ret_dev) {
|
|
||||||
|
|
||||||
struct stat st, st2;
|
|
||||||
const char *t2, *trigger;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(path);
|
|
||||||
assert(ret_dev);
|
|
||||||
|
|
||||||
/* So, the ESP and XBOOTLDR partition are commonly located on an autofs mount. stat() on the
|
|
||||||
* directory won't trigger it, if it is not mounted yet. Let's hence explicitly trigger it here,
|
|
||||||
* before stat()ing */
|
|
||||||
trigger = strjoina(path, "/trigger"); /* Filename doesn't matter... */
|
|
||||||
(void) access(trigger, F_OK);
|
|
||||||
|
|
||||||
if (stat(path, &st) < 0)
|
|
||||||
return log_full_errno((searching && errno == ENOENT) ||
|
|
||||||
(unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
|
|
||||||
"Failed to determine block device node of \"%s\": %m", path);
|
|
||||||
|
|
||||||
if (major(st.st_dev) == 0)
|
|
||||||
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
|
||||||
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
|
|
||||||
"Block device node of \"%s\" is invalid.", path);
|
|
||||||
|
|
||||||
if (path_equal(path, "/")) {
|
|
||||||
/* Let's assume that the root directory of the OS is always the root of its file system
|
|
||||||
* (which technically doesn't have to be the case, but it's close enough, and it's not easy
|
|
||||||
* to be fully correct for it, since we can't look further up than the root dir easily.) */
|
|
||||||
if (ret_dev)
|
|
||||||
*ret_dev = st.st_dev;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
t2 = strjoina(path, "/..");
|
|
||||||
if (stat(t2, &st2) < 0) {
|
|
||||||
if (errno != EACCES)
|
|
||||||
r = -errno;
|
|
||||||
else {
|
|
||||||
_cleanup_free_ char *parent = NULL;
|
|
||||||
|
|
||||||
/* If going via ".." didn't work due to EACCESS, then let's determine the parent path
|
|
||||||
* directly instead. It's not as good, due to symlinks and such, but we can't do
|
|
||||||
* anything better here. */
|
|
||||||
|
|
||||||
parent = dirname_malloc(path);
|
|
||||||
if (!parent)
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
r = RET_NERRNO(stat(parent, &st2));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r < 0)
|
|
||||||
return log_full_errno(unprivileged_mode && r == -EACCES ? LOG_DEBUG : LOG_ERR, r,
|
|
||||||
"Failed to determine block device node of parent of \"%s\": %m", path);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (st.st_dev == st2.st_dev)
|
|
||||||
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
|
||||||
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
|
|
||||||
"Directory \"%s\" is not the root of the file system.", path);
|
|
||||||
|
|
||||||
if (ret_dev)
|
|
||||||
*ret_dev = st.st_dev;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int verify_esp(
|
|
||||||
const char *p,
|
|
||||||
bool searching,
|
|
||||||
bool unprivileged_mode,
|
|
||||||
uint32_t *ret_part,
|
|
||||||
uint64_t *ret_pstart,
|
|
||||||
uint64_t *ret_psize,
|
|
||||||
sd_id128_t *ret_uuid,
|
|
||||||
dev_t *ret_devid) {
|
|
||||||
|
|
||||||
bool relax_checks;
|
|
||||||
dev_t devid;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(p);
|
|
||||||
|
|
||||||
/* This logs about all errors, except:
|
|
||||||
*
|
|
||||||
* -ENOENT → if 'searching' is set, and the dir doesn't exist
|
|
||||||
* -EADDRNOTAVAIL → if 'searching' is set, and the dir doesn't look like an ESP
|
|
||||||
* -EACESS → if 'unprivileged_mode' is set, and we have trouble accessing the thing
|
|
||||||
*/
|
|
||||||
|
|
||||||
relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0;
|
|
||||||
|
|
||||||
/* Non-root user can only check the status, so if an error occurred in the following, it does not cause any
|
|
||||||
* issues. Let's also, silence the error messages. */
|
|
||||||
|
|
||||||
if (!relax_checks) {
|
|
||||||
struct statfs sfs;
|
|
||||||
|
|
||||||
if (statfs(p, &sfs) < 0)
|
|
||||||
/* If we are searching for the mount point, don't generate a log message if we can't find the path */
|
|
||||||
return log_full_errno((searching && errno == ENOENT) ||
|
|
||||||
(unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
|
|
||||||
"Failed to check file system type of \"%s\": %m", p);
|
|
||||||
|
|
||||||
if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC))
|
|
||||||
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
|
||||||
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
|
|
||||||
"File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
|
|
||||||
}
|
|
||||||
|
|
||||||
r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
/* In a container we don't have access to block devices, skip this part of the verification, we trust
|
|
||||||
* the container manager set everything up correctly on its own. */
|
|
||||||
if (detect_container() > 0 || relax_checks)
|
|
||||||
goto finish;
|
|
||||||
|
|
||||||
/* If we are unprivileged we ask udev for the metadata about the partition. If we are privileged we
|
|
||||||
* use blkid instead. Why? Because this code is called from 'bootctl' which is pretty much an
|
|
||||||
* emergency recovery tool that should also work when udev isn't up (i.e. from the emergency shell),
|
|
||||||
* however blkid can't work if we have no privileges to access block devices directly, which is why
|
|
||||||
* we use udev in that case. */
|
|
||||||
if (unprivileged_mode)
|
|
||||||
r = verify_esp_udev(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
|
|
||||||
else
|
|
||||||
r = verify_esp_blkid(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
if (ret_devid)
|
|
||||||
*ret_devid = devid;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
finish:
|
|
||||||
if (ret_part)
|
|
||||||
*ret_part = 0;
|
|
||||||
if (ret_pstart)
|
|
||||||
*ret_pstart = 0;
|
|
||||||
if (ret_psize)
|
|
||||||
*ret_psize = 0;
|
|
||||||
if (ret_uuid)
|
|
||||||
*ret_uuid = SD_ID128_NULL;
|
|
||||||
if (ret_devid)
|
|
||||||
*ret_devid = 0;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int find_esp_and_warn(
|
|
||||||
const char *path,
|
|
||||||
bool unprivileged_mode,
|
|
||||||
char **ret_path,
|
|
||||||
uint32_t *ret_part,
|
|
||||||
uint64_t *ret_pstart,
|
|
||||||
uint64_t *ret_psize,
|
|
||||||
sd_id128_t *ret_uuid,
|
|
||||||
dev_t *ret_devid) {
|
|
||||||
|
|
||||||
int r;
|
|
||||||
|
|
||||||
/* This logs about all errors except:
|
|
||||||
*
|
|
||||||
* -ENOKEY → when we can't find the partition
|
|
||||||
* -EACCESS → when unprivileged_mode is true, and we can't access something
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (path) {
|
|
||||||
r = verify_esp(path, /* searching= */ false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
goto found;
|
|
||||||
}
|
|
||||||
|
|
||||||
path = getenv("SYSTEMD_ESP_PATH");
|
|
||||||
if (path) {
|
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
if (!path_is_valid(path) || !path_is_absolute(path))
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
||||||
"$SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s",
|
|
||||||
path);
|
|
||||||
|
|
||||||
/* Note: when the user explicitly configured things with an env var we won't validate the
|
|
||||||
* path beyond checking it refers to a directory. After all we want this to be useful for
|
|
||||||
* testing. */
|
|
||||||
|
|
||||||
if (stat(path, &st) < 0)
|
|
||||||
return log_error_errno(errno, "Failed to stat '%s': %m", path);
|
|
||||||
if (!S_ISDIR(st.st_mode))
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "ESP path '%s' is not a directory.", path);
|
|
||||||
|
|
||||||
if (ret_part)
|
|
||||||
*ret_part = 0;
|
|
||||||
if (ret_pstart)
|
|
||||||
*ret_pstart = 0;
|
|
||||||
if (ret_psize)
|
|
||||||
*ret_psize = 0;
|
|
||||||
if (ret_uuid)
|
|
||||||
*ret_uuid = SD_ID128_NULL;
|
|
||||||
if (ret_devid)
|
|
||||||
*ret_devid = st.st_dev;
|
|
||||||
|
|
||||||
goto found;
|
|
||||||
}
|
|
||||||
|
|
||||||
FOREACH_STRING(path, "/efi", "/boot", "/boot/efi") {
|
|
||||||
|
|
||||||
r = verify_esp(path, /* searching= */ true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
|
|
||||||
if (r >= 0)
|
|
||||||
goto found;
|
|
||||||
if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* No logging here */
|
|
||||||
return -ENOKEY;
|
|
||||||
|
|
||||||
found:
|
|
||||||
if (ret_path) {
|
|
||||||
char *c;
|
|
||||||
|
|
||||||
c = strdup(path);
|
|
||||||
if (!c)
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
*ret_path = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int verify_xbootldr_blkid(
|
|
||||||
dev_t devid,
|
|
||||||
bool searching,
|
|
||||||
sd_id128_t *ret_uuid) {
|
|
||||||
|
|
||||||
sd_id128_t uuid = SD_ID128_NULL;
|
|
||||||
|
|
||||||
#if HAVE_BLKID
|
|
||||||
_cleanup_(blkid_free_probep) blkid_probe b = NULL;
|
|
||||||
_cleanup_free_ char *node = NULL;
|
|
||||||
const char *v;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
r = device_path_make_major_minor(S_IFBLK, devid, &node);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to format major/minor device path: %m");
|
|
||||||
errno = 0;
|
|
||||||
b = blkid_new_probe_from_filename(node);
|
|
||||||
if (!b)
|
|
||||||
return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
|
|
||||||
|
|
||||||
blkid_probe_enable_partitions(b, 1);
|
|
||||||
blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
r = blkid_do_safeprobe(b);
|
|
||||||
if (r == -2)
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node);
|
|
||||||
else if (r == 1)
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
|
|
||||||
else if (r != 0)
|
|
||||||
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
|
|
||||||
if (r != 0)
|
|
||||||
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node);
|
|
||||||
if (streq(v, "gpt")) {
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
|
|
||||||
if (r != 0)
|
|
||||||
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node);
|
|
||||||
if (id128_equal_string(v, GPT_XBOOTLDR) <= 0)
|
|
||||||
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
|
||||||
searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
|
|
||||||
"File system \"%s\" has wrong type for extended boot loader partition.", node);
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
|
|
||||||
if (r != 0)
|
|
||||||
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
|
|
||||||
r = sd_id128_from_string(v, &uuid);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
|
|
||||||
|
|
||||||
} else if (streq(v, "dos")) {
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
|
|
||||||
if (r != 0)
|
|
||||||
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node);
|
|
||||||
if (!streq(v, "0xea"))
|
|
||||||
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
|
||||||
searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
|
|
||||||
"File system \"%s\" has wrong type for extended boot loader partition.", node);
|
|
||||||
|
|
||||||
} else
|
|
||||||
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
|
||||||
searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
|
|
||||||
"File system \"%s\" is not on a GPT or DOS partition table.", node);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ret_uuid)
|
|
||||||
*ret_uuid = uuid;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int verify_xbootldr_udev(
|
|
||||||
dev_t devid,
|
|
||||||
bool searching,
|
|
||||||
sd_id128_t *ret_uuid) {
|
|
||||||
|
|
||||||
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
|
|
||||||
_cleanup_free_ char *node = NULL;
|
|
||||||
sd_id128_t uuid = SD_ID128_NULL;
|
|
||||||
const char *v;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
r = device_path_make_major_minor(S_IFBLK, devid, &node);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to format major/minor device path: %m");
|
|
||||||
|
|
||||||
r = sd_device_new_from_devnum(&d, 'b', devid);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to get device from device number: %m");
|
|
||||||
|
|
||||||
r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to get device property: %m");
|
|
||||||
|
|
||||||
if (streq(v, "gpt")) {
|
|
||||||
|
|
||||||
r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to get device property: %m");
|
|
||||||
if (id128_equal_string(v, GPT_XBOOTLDR))
|
|
||||||
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
|
||||||
searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
|
|
||||||
"File system \"%s\" has wrong type for extended boot loader partition.", node);
|
|
||||||
|
|
||||||
r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to get device property: %m");
|
|
||||||
r = sd_id128_from_string(v, &uuid);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
|
|
||||||
|
|
||||||
} else if (streq(v, "dos")) {
|
|
||||||
|
|
||||||
r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
|
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to get device property: %m");
|
|
||||||
if (!streq(v, "0xea"))
|
|
||||||
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
|
||||||
searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
|
|
||||||
"File system \"%s\" has wrong type for extended boot loader partition.", node);
|
|
||||||
} else
|
|
||||||
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
|
||||||
searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
|
|
||||||
"File system \"%s\" is not on a GPT or DOS partition table.", node);
|
|
||||||
|
|
||||||
if (ret_uuid)
|
|
||||||
*ret_uuid = uuid;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int verify_xbootldr(
|
|
||||||
const char *p,
|
|
||||||
bool searching,
|
|
||||||
bool unprivileged_mode,
|
|
||||||
sd_id128_t *ret_uuid,
|
|
||||||
dev_t *ret_devid) {
|
|
||||||
|
|
||||||
bool relax_checks;
|
|
||||||
dev_t devid;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(p);
|
|
||||||
|
|
||||||
relax_checks = getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0;
|
|
||||||
|
|
||||||
r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
if (detect_container() > 0 || relax_checks)
|
|
||||||
goto finish;
|
|
||||||
|
|
||||||
if (unprivileged_mode)
|
|
||||||
r = verify_xbootldr_udev(devid, searching, ret_uuid);
|
|
||||||
else
|
|
||||||
r = verify_xbootldr_blkid(devid, searching, ret_uuid);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
if (ret_devid)
|
|
||||||
*ret_devid = devid;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
finish:
|
|
||||||
if (ret_uuid)
|
|
||||||
*ret_uuid = SD_ID128_NULL;
|
|
||||||
if (ret_devid)
|
|
||||||
*ret_devid = 0;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int find_xbootldr_and_warn(
|
|
||||||
const char *path,
|
|
||||||
bool unprivileged_mode,
|
|
||||||
char **ret_path,
|
|
||||||
sd_id128_t *ret_uuid,
|
|
||||||
dev_t *ret_devid) {
|
|
||||||
|
|
||||||
int r;
|
|
||||||
|
|
||||||
/* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */
|
|
||||||
|
|
||||||
if (path) {
|
|
||||||
r = verify_xbootldr(path, /* searching= */ false, unprivileged_mode, ret_uuid, ret_devid);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
goto found;
|
|
||||||
}
|
|
||||||
|
|
||||||
path = getenv("SYSTEMD_XBOOTLDR_PATH");
|
|
||||||
if (path) {
|
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
if (!path_is_valid(path) || !path_is_absolute(path))
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
||||||
"$SYSTEMD_XBOOTLDR_PATH does not refer to absolute path, refusing to use it: %s",
|
|
||||||
path);
|
|
||||||
|
|
||||||
if (stat(path, &st) < 0)
|
|
||||||
return log_error_errno(errno, "Failed to stat '%s': %m", path);
|
|
||||||
if (!S_ISDIR(st.st_mode))
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "XBOOTLDR path '%s' is not a directory.", path);
|
|
||||||
|
|
||||||
if (ret_uuid)
|
|
||||||
*ret_uuid = SD_ID128_NULL;
|
|
||||||
if (ret_devid)
|
|
||||||
*ret_devid = st.st_dev;
|
|
||||||
|
|
||||||
goto found;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = verify_xbootldr("/boot", true, unprivileged_mode, ret_uuid, ret_devid);
|
|
||||||
if (r >= 0) {
|
|
||||||
path = "/boot";
|
|
||||||
goto found;
|
|
||||||
}
|
|
||||||
if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
|
|
||||||
return r;
|
|
||||||
|
|
||||||
return -ENOKEY;
|
|
||||||
|
|
||||||
found:
|
|
||||||
if (ret_path) {
|
|
||||||
char *c;
|
|
||||||
|
|
||||||
c = strdup(path);
|
|
||||||
if (!c)
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
*ret_path = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -2,12 +2,11 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include "sd-id128.h"
|
|
||||||
|
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
|
|
||||||
typedef enum BootEntryType {
|
typedef enum BootEntryType {
|
||||||
@ -28,6 +27,7 @@ typedef struct BootEntry {
|
|||||||
char *root; /* The root path in which the drop-in was found, i.e. to which 'kernel', 'efi' and 'initrd' are relative */
|
char *root; /* The root path in which the drop-in was found, i.e. to which 'kernel', 'efi' and 'initrd' are relative */
|
||||||
char *title;
|
char *title;
|
||||||
char *show_title;
|
char *show_title;
|
||||||
|
char *sort_key;
|
||||||
char *version;
|
char *version;
|
||||||
char *machine_id;
|
char *machine_id;
|
||||||
char *architecture;
|
char *architecture;
|
||||||
@ -90,6 +90,3 @@ static inline const char* boot_entry_title(const BootEntry *entry) {
|
|||||||
|
|
||||||
return entry->show_title ?: entry->title ?: entry->id;
|
return entry->show_title ?: entry->title ?: entry->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
int find_esp_and_warn(const char *path, bool unprivileged_mode, char **ret_path, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid, dev_t *ret_devid);
|
|
||||||
int find_xbootldr_and_warn(const char *path, bool unprivileged_mode, char **ret_path,sd_id128_t *ret_uuid, dev_t *ret_devid);
|
|
||||||
|
|||||||
@ -134,9 +134,8 @@ int clock_reset_timewarp(void) {
|
|||||||
#define EPOCH_FILE "/usr/lib/clock-epoch"
|
#define EPOCH_FILE "/usr/lib/clock-epoch"
|
||||||
|
|
||||||
int clock_apply_epoch(ClockChangeDirection *ret_attempted_change) {
|
int clock_apply_epoch(ClockChangeDirection *ret_attempted_change) {
|
||||||
struct stat st;
|
|
||||||
struct timespec ts;
|
|
||||||
usec_t epoch_usec, now_usec;
|
usec_t epoch_usec, now_usec;
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
/* NB: we update *ret_attempted_change in *all* cases, both
|
/* NB: we update *ret_attempted_change in *all* cases, both
|
||||||
* on success and failure, to indicate what we intended to do! */
|
* on success and failure, to indicate what we intended to do! */
|
||||||
@ -161,7 +160,7 @@ int clock_apply_epoch(ClockChangeDirection *ret_attempted_change) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, epoch_usec)) < 0)
|
if (clock_settime(CLOCK_REALTIME, TIMESPEC_STORE(epoch_usec)) < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
709
src/shared/find-esp.c
Normal file
709
src/shared/find-esp.c
Normal file
@ -0,0 +1,709 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#include <linux/magic.h>
|
||||||
|
|
||||||
|
#include "sd-device.h"
|
||||||
|
|
||||||
|
#include "alloc-util.h"
|
||||||
|
#include "blkid-util.h"
|
||||||
|
#include "env-util.h"
|
||||||
|
#include "errno-util.h"
|
||||||
|
#include "find-esp.h"
|
||||||
|
#include "gpt.h"
|
||||||
|
#include "id128-util.h"
|
||||||
|
#include "parse-util.h"
|
||||||
|
#include "path-util.h"
|
||||||
|
#include "stat-util.h"
|
||||||
|
#include "string-util.h"
|
||||||
|
#include "virt.h"
|
||||||
|
|
||||||
|
static int verify_esp_blkid(
|
||||||
|
dev_t devid,
|
||||||
|
bool searching,
|
||||||
|
uint32_t *ret_part,
|
||||||
|
uint64_t *ret_pstart,
|
||||||
|
uint64_t *ret_psize,
|
||||||
|
sd_id128_t *ret_uuid) {
|
||||||
|
|
||||||
|
sd_id128_t uuid = SD_ID128_NULL;
|
||||||
|
uint64_t pstart = 0, psize = 0;
|
||||||
|
uint32_t part = 0;
|
||||||
|
|
||||||
|
#if HAVE_BLKID
|
||||||
|
_cleanup_(blkid_free_probep) blkid_probe b = NULL;
|
||||||
|
_cleanup_free_ char *node = NULL;
|
||||||
|
const char *v;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = device_path_make_major_minor(S_IFBLK, devid, &node);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to format major/minor device path: %m");
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
b = blkid_new_probe_from_filename(node);
|
||||||
|
if (!b)
|
||||||
|
return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
|
||||||
|
|
||||||
|
blkid_probe_enable_superblocks(b, 1);
|
||||||
|
blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
|
||||||
|
blkid_probe_enable_partitions(b, 1);
|
||||||
|
blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
r = blkid_do_safeprobe(b);
|
||||||
|
if (r == -2)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node);
|
||||||
|
else if (r == 1)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
|
||||||
|
else if (r != 0)
|
||||||
|
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
|
||||||
|
|
||||||
|
r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
|
||||||
|
if (r != 0)
|
||||||
|
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
||||||
|
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
|
||||||
|
"No filesystem found on \"%s\": %m", node);
|
||||||
|
if (!streq(v, "vfat"))
|
||||||
|
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
||||||
|
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
|
||||||
|
"File system \"%s\" is not FAT.", node);
|
||||||
|
|
||||||
|
r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
|
||||||
|
if (r != 0)
|
||||||
|
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
||||||
|
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
|
||||||
|
"File system \"%s\" is not located on a partitioned block device.", node);
|
||||||
|
if (!streq(v, "gpt"))
|
||||||
|
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
||||||
|
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
|
||||||
|
"File system \"%s\" is not on a GPT partition table.", node);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
|
||||||
|
if (r != 0)
|
||||||
|
return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID of \"%s\": %m", node);
|
||||||
|
if (id128_equal_string(v, GPT_ESP) <= 0)
|
||||||
|
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
||||||
|
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
|
||||||
|
"File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
|
||||||
|
if (r != 0)
|
||||||
|
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
|
||||||
|
r = sd_id128_from_string(v, &uuid);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
|
||||||
|
if (r != 0)
|
||||||
|
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition number of \"%s\": %m", node);
|
||||||
|
r = safe_atou32(v, &part);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL);
|
||||||
|
if (r != 0)
|
||||||
|
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition offset of \"%s\": %m", node);
|
||||||
|
r = safe_atou64(v, &pstart);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL);
|
||||||
|
if (r != 0)
|
||||||
|
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition size of \"%s\": %m", node);
|
||||||
|
r = safe_atou64(v, &psize);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (ret_part)
|
||||||
|
*ret_part = part;
|
||||||
|
if (ret_pstart)
|
||||||
|
*ret_pstart = pstart;
|
||||||
|
if (ret_psize)
|
||||||
|
*ret_psize = psize;
|
||||||
|
if (ret_uuid)
|
||||||
|
*ret_uuid = uuid;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int verify_esp_udev(
|
||||||
|
dev_t devid,
|
||||||
|
bool searching,
|
||||||
|
uint32_t *ret_part,
|
||||||
|
uint64_t *ret_pstart,
|
||||||
|
uint64_t *ret_psize,
|
||||||
|
sd_id128_t *ret_uuid) {
|
||||||
|
|
||||||
|
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
|
||||||
|
_cleanup_free_ char *node = NULL;
|
||||||
|
sd_id128_t uuid = SD_ID128_NULL;
|
||||||
|
uint64_t pstart = 0, psize = 0;
|
||||||
|
uint32_t part = 0;
|
||||||
|
const char *v;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = device_path_make_major_minor(S_IFBLK, devid, &node);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to format major/minor device path: %m");
|
||||||
|
|
||||||
|
r = sd_device_new_from_devnum(&d, 'b', devid);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to get device from device number: %m");
|
||||||
|
|
||||||
|
r = sd_device_get_property_value(d, "ID_FS_TYPE", &v);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to get device property: %m");
|
||||||
|
if (!streq(v, "vfat"))
|
||||||
|
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
||||||
|
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
|
||||||
|
"File system \"%s\" is not FAT.", node );
|
||||||
|
|
||||||
|
r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to get device property: %m");
|
||||||
|
if (!streq(v, "gpt"))
|
||||||
|
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
||||||
|
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
|
||||||
|
"File system \"%s\" is not on a GPT partition table.", node);
|
||||||
|
|
||||||
|
r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to get device property: %m");
|
||||||
|
if (id128_equal_string(v, GPT_ESP) <= 0)
|
||||||
|
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
||||||
|
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
|
||||||
|
"File system \"%s\" has wrong type for an EFI System Partition (ESP).", node);
|
||||||
|
|
||||||
|
r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to get device property: %m");
|
||||||
|
r = sd_id128_from_string(v, &uuid);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
|
||||||
|
|
||||||
|
r = sd_device_get_property_value(d, "ID_PART_ENTRY_NUMBER", &v);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to get device property: %m");
|
||||||
|
r = safe_atou32(v, &part);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field.");
|
||||||
|
|
||||||
|
r = sd_device_get_property_value(d, "ID_PART_ENTRY_OFFSET", &v);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to get device property: %m");
|
||||||
|
r = safe_atou64(v, &pstart);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field.");
|
||||||
|
|
||||||
|
r = sd_device_get_property_value(d, "ID_PART_ENTRY_SIZE", &v);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to get device property: %m");
|
||||||
|
r = safe_atou64(v, &psize);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
|
||||||
|
|
||||||
|
if (ret_part)
|
||||||
|
*ret_part = part;
|
||||||
|
if (ret_pstart)
|
||||||
|
*ret_pstart = pstart;
|
||||||
|
if (ret_psize)
|
||||||
|
*ret_psize = psize;
|
||||||
|
if (ret_uuid)
|
||||||
|
*ret_uuid = uuid;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int verify_fsroot_dir(
|
||||||
|
const char *path,
|
||||||
|
bool searching,
|
||||||
|
bool unprivileged_mode,
|
||||||
|
dev_t *ret_dev) {
|
||||||
|
|
||||||
|
struct stat st, st2;
|
||||||
|
const char *t2, *trigger;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(path);
|
||||||
|
assert(ret_dev);
|
||||||
|
|
||||||
|
/* So, the ESP and XBOOTLDR partition are commonly located on an autofs mount. stat() on the
|
||||||
|
* directory won't trigger it, if it is not mounted yet. Let's hence explicitly trigger it here,
|
||||||
|
* before stat()ing */
|
||||||
|
trigger = strjoina(path, "/trigger"); /* Filename doesn't matter... */
|
||||||
|
(void) access(trigger, F_OK);
|
||||||
|
|
||||||
|
if (stat(path, &st) < 0)
|
||||||
|
return log_full_errno((searching && errno == ENOENT) ||
|
||||||
|
(unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
|
||||||
|
"Failed to determine block device node of \"%s\": %m", path);
|
||||||
|
|
||||||
|
if (major(st.st_dev) == 0)
|
||||||
|
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
||||||
|
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
|
||||||
|
"Block device node of \"%s\" is invalid.", path);
|
||||||
|
|
||||||
|
if (path_equal(path, "/")) {
|
||||||
|
/* Let's assume that the root directory of the OS is always the root of its file system
|
||||||
|
* (which technically doesn't have to be the case, but it's close enough, and it's not easy
|
||||||
|
* to be fully correct for it, since we can't look further up than the root dir easily.) */
|
||||||
|
if (ret_dev)
|
||||||
|
*ret_dev = st.st_dev;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
t2 = strjoina(path, "/..");
|
||||||
|
if (stat(t2, &st2) < 0) {
|
||||||
|
if (errno != EACCES)
|
||||||
|
r = -errno;
|
||||||
|
else {
|
||||||
|
_cleanup_free_ char *parent = NULL;
|
||||||
|
|
||||||
|
/* If going via ".." didn't work due to EACCESS, then let's determine the parent path
|
||||||
|
* directly instead. It's not as good, due to symlinks and such, but we can't do
|
||||||
|
* anything better here. */
|
||||||
|
|
||||||
|
parent = dirname_malloc(path);
|
||||||
|
if (!parent)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
r = RET_NERRNO(stat(parent, &st2));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r < 0)
|
||||||
|
return log_full_errno(unprivileged_mode && r == -EACCES ? LOG_DEBUG : LOG_ERR, r,
|
||||||
|
"Failed to determine block device node of parent of \"%s\": %m", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (st.st_dev == st2.st_dev)
|
||||||
|
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
||||||
|
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
|
||||||
|
"Directory \"%s\" is not the root of the file system.", path);
|
||||||
|
|
||||||
|
if (ret_dev)
|
||||||
|
*ret_dev = st.st_dev;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int verify_esp(
|
||||||
|
const char *p,
|
||||||
|
bool searching,
|
||||||
|
bool unprivileged_mode,
|
||||||
|
uint32_t *ret_part,
|
||||||
|
uint64_t *ret_pstart,
|
||||||
|
uint64_t *ret_psize,
|
||||||
|
sd_id128_t *ret_uuid,
|
||||||
|
dev_t *ret_devid) {
|
||||||
|
|
||||||
|
bool relax_checks;
|
||||||
|
dev_t devid;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(p);
|
||||||
|
|
||||||
|
/* This logs about all errors, except:
|
||||||
|
*
|
||||||
|
* -ENOENT → if 'searching' is set, and the dir doesn't exist
|
||||||
|
* -EADDRNOTAVAIL → if 'searching' is set, and the dir doesn't look like an ESP
|
||||||
|
* -EACESS → if 'unprivileged_mode' is set, and we have trouble accessing the thing
|
||||||
|
*/
|
||||||
|
|
||||||
|
relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0;
|
||||||
|
|
||||||
|
/* Non-root user can only check the status, so if an error occurred in the following, it does not cause any
|
||||||
|
* issues. Let's also, silence the error messages. */
|
||||||
|
|
||||||
|
if (!relax_checks) {
|
||||||
|
struct statfs sfs;
|
||||||
|
|
||||||
|
if (statfs(p, &sfs) < 0)
|
||||||
|
/* If we are searching for the mount point, don't generate a log message if we can't find the path */
|
||||||
|
return log_full_errno((searching && errno == ENOENT) ||
|
||||||
|
(unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno,
|
||||||
|
"Failed to check file system type of \"%s\": %m", p);
|
||||||
|
|
||||||
|
if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC))
|
||||||
|
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
||||||
|
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
|
||||||
|
"File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
/* In a container we don't have access to block devices, skip this part of the verification, we trust
|
||||||
|
* the container manager set everything up correctly on its own. */
|
||||||
|
if (detect_container() > 0 || relax_checks)
|
||||||
|
goto finish;
|
||||||
|
|
||||||
|
/* If we are unprivileged we ask udev for the metadata about the partition. If we are privileged we
|
||||||
|
* use blkid instead. Why? Because this code is called from 'bootctl' which is pretty much an
|
||||||
|
* emergency recovery tool that should also work when udev isn't up (i.e. from the emergency shell),
|
||||||
|
* however blkid can't work if we have no privileges to access block devices directly, which is why
|
||||||
|
* we use udev in that case. */
|
||||||
|
if (unprivileged_mode)
|
||||||
|
r = verify_esp_udev(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
|
||||||
|
else
|
||||||
|
r = verify_esp_blkid(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (ret_devid)
|
||||||
|
*ret_devid = devid;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
finish:
|
||||||
|
if (ret_part)
|
||||||
|
*ret_part = 0;
|
||||||
|
if (ret_pstart)
|
||||||
|
*ret_pstart = 0;
|
||||||
|
if (ret_psize)
|
||||||
|
*ret_psize = 0;
|
||||||
|
if (ret_uuid)
|
||||||
|
*ret_uuid = SD_ID128_NULL;
|
||||||
|
if (ret_devid)
|
||||||
|
*ret_devid = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int find_esp_and_warn(
|
||||||
|
const char *path,
|
||||||
|
bool unprivileged_mode,
|
||||||
|
char **ret_path,
|
||||||
|
uint32_t *ret_part,
|
||||||
|
uint64_t *ret_pstart,
|
||||||
|
uint64_t *ret_psize,
|
||||||
|
sd_id128_t *ret_uuid,
|
||||||
|
dev_t *ret_devid) {
|
||||||
|
|
||||||
|
int r;
|
||||||
|
|
||||||
|
/* This logs about all errors except:
|
||||||
|
*
|
||||||
|
* -ENOKEY → when we can't find the partition
|
||||||
|
* -EACCESS → when unprivileged_mode is true, and we can't access something
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (path) {
|
||||||
|
r = verify_esp(path, /* searching= */ false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = getenv("SYSTEMD_ESP_PATH");
|
||||||
|
if (path) {
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (!path_is_valid(path) || !path_is_absolute(path))
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
"$SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s",
|
||||||
|
path);
|
||||||
|
|
||||||
|
/* Note: when the user explicitly configured things with an env var we won't validate the
|
||||||
|
* path beyond checking it refers to a directory. After all we want this to be useful for
|
||||||
|
* testing. */
|
||||||
|
|
||||||
|
if (stat(path, &st) < 0)
|
||||||
|
return log_error_errno(errno, "Failed to stat '%s': %m", path);
|
||||||
|
if (!S_ISDIR(st.st_mode))
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "ESP path '%s' is not a directory.", path);
|
||||||
|
|
||||||
|
if (ret_part)
|
||||||
|
*ret_part = 0;
|
||||||
|
if (ret_pstart)
|
||||||
|
*ret_pstart = 0;
|
||||||
|
if (ret_psize)
|
||||||
|
*ret_psize = 0;
|
||||||
|
if (ret_uuid)
|
||||||
|
*ret_uuid = SD_ID128_NULL;
|
||||||
|
if (ret_devid)
|
||||||
|
*ret_devid = st.st_dev;
|
||||||
|
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
|
||||||
|
FOREACH_STRING(path, "/efi", "/boot", "/boot/efi") {
|
||||||
|
|
||||||
|
r = verify_esp(path, /* searching= */ true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
|
||||||
|
if (r >= 0)
|
||||||
|
goto found;
|
||||||
|
if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No logging here */
|
||||||
|
return -ENOKEY;
|
||||||
|
|
||||||
|
found:
|
||||||
|
if (ret_path) {
|
||||||
|
char *c;
|
||||||
|
|
||||||
|
c = strdup(path);
|
||||||
|
if (!c)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
*ret_path = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int verify_xbootldr_blkid(
|
||||||
|
dev_t devid,
|
||||||
|
bool searching,
|
||||||
|
sd_id128_t *ret_uuid) {
|
||||||
|
|
||||||
|
sd_id128_t uuid = SD_ID128_NULL;
|
||||||
|
|
||||||
|
#if HAVE_BLKID
|
||||||
|
_cleanup_(blkid_free_probep) blkid_probe b = NULL;
|
||||||
|
_cleanup_free_ char *node = NULL;
|
||||||
|
const char *v;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = device_path_make_major_minor(S_IFBLK, devid, &node);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to format major/minor device path: %m");
|
||||||
|
errno = 0;
|
||||||
|
b = blkid_new_probe_from_filename(node);
|
||||||
|
if (!b)
|
||||||
|
return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node);
|
||||||
|
|
||||||
|
blkid_probe_enable_partitions(b, 1);
|
||||||
|
blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
r = blkid_do_safeprobe(b);
|
||||||
|
if (r == -2)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node);
|
||||||
|
else if (r == 1)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node);
|
||||||
|
else if (r != 0)
|
||||||
|
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
|
||||||
|
if (r != 0)
|
||||||
|
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node);
|
||||||
|
if (streq(v, "gpt")) {
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
|
||||||
|
if (r != 0)
|
||||||
|
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node);
|
||||||
|
if (id128_equal_string(v, GPT_XBOOTLDR) <= 0)
|
||||||
|
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
||||||
|
searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
|
||||||
|
"File system \"%s\" has wrong type for extended boot loader partition.", node);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
|
||||||
|
if (r != 0)
|
||||||
|
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node);
|
||||||
|
r = sd_id128_from_string(v, &uuid);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
|
||||||
|
|
||||||
|
} else if (streq(v, "dos")) {
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
|
||||||
|
if (r != 0)
|
||||||
|
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node);
|
||||||
|
if (!streq(v, "0xea"))
|
||||||
|
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
||||||
|
searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
|
||||||
|
"File system \"%s\" has wrong type for extended boot loader partition.", node);
|
||||||
|
|
||||||
|
} else
|
||||||
|
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
||||||
|
searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
|
||||||
|
"File system \"%s\" is not on a GPT or DOS partition table.", node);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (ret_uuid)
|
||||||
|
*ret_uuid = uuid;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int verify_xbootldr_udev(
|
||||||
|
dev_t devid,
|
||||||
|
bool searching,
|
||||||
|
sd_id128_t *ret_uuid) {
|
||||||
|
|
||||||
|
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
|
||||||
|
_cleanup_free_ char *node = NULL;
|
||||||
|
sd_id128_t uuid = SD_ID128_NULL;
|
||||||
|
const char *v;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = device_path_make_major_minor(S_IFBLK, devid, &node);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to format major/minor device path: %m");
|
||||||
|
|
||||||
|
r = sd_device_new_from_devnum(&d, 'b', devid);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to get device from device number: %m");
|
||||||
|
|
||||||
|
r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to get device property: %m");
|
||||||
|
|
||||||
|
if (streq(v, "gpt")) {
|
||||||
|
|
||||||
|
r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to get device property: %m");
|
||||||
|
if (id128_equal_string(v, GPT_XBOOTLDR))
|
||||||
|
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
||||||
|
searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
|
||||||
|
"File system \"%s\" has wrong type for extended boot loader partition.", node);
|
||||||
|
|
||||||
|
r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to get device property: %m");
|
||||||
|
r = sd_id128_from_string(v, &uuid);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v);
|
||||||
|
|
||||||
|
} else if (streq(v, "dos")) {
|
||||||
|
|
||||||
|
r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to get device property: %m");
|
||||||
|
if (!streq(v, "0xea"))
|
||||||
|
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
||||||
|
searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
|
||||||
|
"File system \"%s\" has wrong type for extended boot loader partition.", node);
|
||||||
|
} else
|
||||||
|
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
|
||||||
|
searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV),
|
||||||
|
"File system \"%s\" is not on a GPT or DOS partition table.", node);
|
||||||
|
|
||||||
|
if (ret_uuid)
|
||||||
|
*ret_uuid = uuid;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int verify_xbootldr(
|
||||||
|
const char *p,
|
||||||
|
bool searching,
|
||||||
|
bool unprivileged_mode,
|
||||||
|
sd_id128_t *ret_uuid,
|
||||||
|
dev_t *ret_devid) {
|
||||||
|
|
||||||
|
bool relax_checks;
|
||||||
|
dev_t devid;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(p);
|
||||||
|
|
||||||
|
relax_checks = getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0;
|
||||||
|
|
||||||
|
r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (detect_container() > 0 || relax_checks)
|
||||||
|
goto finish;
|
||||||
|
|
||||||
|
if (unprivileged_mode)
|
||||||
|
r = verify_xbootldr_udev(devid, searching, ret_uuid);
|
||||||
|
else
|
||||||
|
r = verify_xbootldr_blkid(devid, searching, ret_uuid);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (ret_devid)
|
||||||
|
*ret_devid = devid;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
finish:
|
||||||
|
if (ret_uuid)
|
||||||
|
*ret_uuid = SD_ID128_NULL;
|
||||||
|
if (ret_devid)
|
||||||
|
*ret_devid = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int find_xbootldr_and_warn(
|
||||||
|
const char *path,
|
||||||
|
bool unprivileged_mode,
|
||||||
|
char **ret_path,
|
||||||
|
sd_id128_t *ret_uuid,
|
||||||
|
dev_t *ret_devid) {
|
||||||
|
|
||||||
|
int r;
|
||||||
|
|
||||||
|
/* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */
|
||||||
|
|
||||||
|
if (path) {
|
||||||
|
r = verify_xbootldr(path, /* searching= */ false, unprivileged_mode, ret_uuid, ret_devid);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = getenv("SYSTEMD_XBOOTLDR_PATH");
|
||||||
|
if (path) {
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (!path_is_valid(path) || !path_is_absolute(path))
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
"$SYSTEMD_XBOOTLDR_PATH does not refer to absolute path, refusing to use it: %s",
|
||||||
|
path);
|
||||||
|
|
||||||
|
if (stat(path, &st) < 0)
|
||||||
|
return log_error_errno(errno, "Failed to stat '%s': %m", path);
|
||||||
|
if (!S_ISDIR(st.st_mode))
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "XBOOTLDR path '%s' is not a directory.", path);
|
||||||
|
|
||||||
|
if (ret_uuid)
|
||||||
|
*ret_uuid = SD_ID128_NULL;
|
||||||
|
if (ret_devid)
|
||||||
|
*ret_devid = st.st_dev;
|
||||||
|
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = verify_xbootldr("/boot", true, unprivileged_mode, ret_uuid, ret_devid);
|
||||||
|
if (r >= 0) {
|
||||||
|
path = "/boot";
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return -ENOKEY;
|
||||||
|
|
||||||
|
found:
|
||||||
|
if (ret_path) {
|
||||||
|
char *c;
|
||||||
|
|
||||||
|
c = strdup(path);
|
||||||
|
if (!c)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
*ret_path = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
12
src/shared/find-esp.h
Normal file
12
src/shared/find-esp.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "sd-id128.h"
|
||||||
|
|
||||||
|
int find_esp_and_warn(const char *path, bool unprivileged_mode, char **ret_path, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid, dev_t *ret_devid);
|
||||||
|
int find_xbootldr_and_warn(const char *path, bool unprivileged_mode, char **ret_path, sd_id128_t *ret_uuid, dev_t *ret_devid);
|
||||||
@ -125,6 +125,8 @@ shared_sources = files(
|
|||||||
'fdset.h',
|
'fdset.h',
|
||||||
'fileio-label.c',
|
'fileio-label.c',
|
||||||
'fileio-label.h',
|
'fileio-label.h',
|
||||||
|
'find-esp.c',
|
||||||
|
'find-esp.h',
|
||||||
'firewall-util-nft.c',
|
'firewall-util-nft.c',
|
||||||
'firewall-util-private.h',
|
'firewall-util-private.h',
|
||||||
'firewall-util.c',
|
'firewall-util.c',
|
||||||
@ -176,8 +178,8 @@ shared_sources = files(
|
|||||||
'json.h',
|
'json.h',
|
||||||
'kbd-util.c',
|
'kbd-util.c',
|
||||||
'kbd-util.h',
|
'kbd-util.h',
|
||||||
'keyring-util.h',
|
|
||||||
'keyring-util.c',
|
'keyring-util.c',
|
||||||
|
'keyring-util.h',
|
||||||
'killall.c',
|
'killall.c',
|
||||||
'killall.h',
|
'killall.h',
|
||||||
'label.c',
|
'label.c',
|
||||||
|
|||||||
@ -513,6 +513,8 @@ tests += [
|
|||||||
|
|
||||||
[files('test-strbuf.c')],
|
[files('test-strbuf.c')],
|
||||||
|
|
||||||
|
[files('test-bootspec.c')],
|
||||||
|
|
||||||
[files('test-strv.c')],
|
[files('test-strv.c')],
|
||||||
|
|
||||||
[files('test-path-util.c')],
|
[files('test-path-util.c')],
|
||||||
|
|||||||
96
src/test/test-bootspec.c
Normal file
96
src/test/test-bootspec.c
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#include "bootspec.h"
|
||||||
|
#include "fileio.h"
|
||||||
|
#include "path-util.h"
|
||||||
|
#include "rm-rf.h"
|
||||||
|
#include "tests.h"
|
||||||
|
#include "tmpfile-util.h"
|
||||||
|
|
||||||
|
TEST_RET(bootspec_sort) {
|
||||||
|
|
||||||
|
static const struct {
|
||||||
|
const char *fname;
|
||||||
|
const char *contents;
|
||||||
|
} entries[] = {
|
||||||
|
{
|
||||||
|
.fname = "a-10.conf",
|
||||||
|
.contents =
|
||||||
|
"title A\n"
|
||||||
|
"version 10\n"
|
||||||
|
"machine-id dd235d00696545768f6f693bfd23b15f\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.fname = "a-5.conf",
|
||||||
|
.contents =
|
||||||
|
"title A\n"
|
||||||
|
"version 5\n"
|
||||||
|
"machine-id dd235d00696545768f6f693bfd23b15f\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.fname = "b.conf",
|
||||||
|
.contents =
|
||||||
|
"title B\n"
|
||||||
|
"version 3\n"
|
||||||
|
"machine-id b75451ad92f94feeab50b0b442768dbd\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.fname = "c.conf",
|
||||||
|
.contents =
|
||||||
|
"title C\n"
|
||||||
|
"sort-key xxxx\n"
|
||||||
|
"version 5\n"
|
||||||
|
"machine-id 309de666fd5044268a9a26541ac93176\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.fname = "cx.conf",
|
||||||
|
.contents =
|
||||||
|
"title C\n"
|
||||||
|
"sort-key xxxx\n"
|
||||||
|
"version 10\n"
|
||||||
|
"machine-id 309de666fd5044268a9a26541ac93176\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.fname = "d.conf",
|
||||||
|
.contents =
|
||||||
|
"title D\n"
|
||||||
|
"sort-key kkkk\n"
|
||||||
|
"version 100\n"
|
||||||
|
"machine-id 81c6e3147cf544c19006af023e22b292\n",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
_cleanup_(rm_rf_physical_and_freep) char *d = NULL;
|
||||||
|
_cleanup_(boot_config_free) BootConfig config = {};
|
||||||
|
|
||||||
|
assert_se(mkdtemp_malloc("/tmp/bootspec-testXXXXXX", &d) >= 0);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ELEMENTSOF(entries); i++) {
|
||||||
|
_cleanup_free_ char *j = NULL;
|
||||||
|
|
||||||
|
j = path_join(d, "/loader/entries/", entries[i].fname);
|
||||||
|
assert_se(j);
|
||||||
|
|
||||||
|
assert_se(write_string_file(j, entries[i].contents, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_se(boot_entries_load_config(d, NULL, &config) >= 0);
|
||||||
|
|
||||||
|
assert_se(config.n_entries == 6);
|
||||||
|
|
||||||
|
/* First, because has sort key, and its the lowest one */
|
||||||
|
assert_se(streq(config.entries[0].id, "d.conf"));
|
||||||
|
|
||||||
|
/* These two have a sort key, and newest must be first */
|
||||||
|
assert_se(streq(config.entries[1].id, "cx.conf"));
|
||||||
|
assert_se(streq(config.entries[2].id, "c.conf"));
|
||||||
|
|
||||||
|
/* The following ones have no sort key, hence order by version compared ids, lowest first */
|
||||||
|
assert_se(streq(config.entries[3].id, "a-5.conf"));
|
||||||
|
assert_se(streq(config.entries[4].id, "a-10.conf"));
|
||||||
|
assert_se(streq(config.entries[5].id, "b.conf"));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_TEST_MAIN(LOG_INFO);
|
||||||
@ -72,13 +72,12 @@ static int load_clock_timestamp(uid_t uid, gid_t gid) {
|
|||||||
settime:
|
settime:
|
||||||
ct = now(CLOCK_REALTIME);
|
ct = now(CLOCK_REALTIME);
|
||||||
if (ct < min) {
|
if (ct < min) {
|
||||||
struct timespec ts;
|
|
||||||
char date[FORMAT_TIMESTAMP_MAX];
|
char date[FORMAT_TIMESTAMP_MAX];
|
||||||
|
|
||||||
log_info("System clock time unset or jumped backwards, restoring from recorded timestamp: %s",
|
log_info("System clock time unset or jumped backwards, restoring from recorded timestamp: %s",
|
||||||
format_timestamp(date, sizeof(date), min));
|
format_timestamp(date, sizeof(date), min));
|
||||||
|
|
||||||
if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, min)) < 0)
|
if (clock_settime(CLOCK_REALTIME, TIMESPEC_STORE(min)) < 0)
|
||||||
log_error_errno(errno, "Failed to restore system clock, ignoring: %m");
|
log_error_errno(errno, "Failed to restore system clock, ignoring: %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -581,8 +581,6 @@ static int ask_on_this_console(const char *tty, pid_t *ret_pid, char **arguments
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void terminate_agents(Set *pids) {
|
static void terminate_agents(Set *pids) {
|
||||||
struct timespec ts;
|
|
||||||
siginfo_t status = {};
|
|
||||||
sigset_t set;
|
sigset_t set;
|
||||||
void *p;
|
void *p;
|
||||||
int r, signum;
|
int r, signum;
|
||||||
@ -599,11 +597,10 @@ static void terminate_agents(Set *pids) {
|
|||||||
*/
|
*/
|
||||||
assert_se(sigemptyset(&set) >= 0);
|
assert_se(sigemptyset(&set) >= 0);
|
||||||
assert_se(sigaddset(&set, SIGCHLD) >= 0);
|
assert_se(sigaddset(&set, SIGCHLD) >= 0);
|
||||||
timespec_store(&ts, 50 * USEC_PER_MSEC);
|
|
||||||
|
|
||||||
while (!set_isempty(pids)) {
|
while (!set_isempty(pids)) {
|
||||||
|
siginfo_t status = {};
|
||||||
|
|
||||||
zero(status);
|
|
||||||
r = waitid(P_ALL, 0, &status, WEXITED|WNOHANG);
|
r = waitid(P_ALL, 0, &status, WEXITED|WNOHANG);
|
||||||
if (r < 0 && errno == EINTR)
|
if (r < 0 && errno == EINTR)
|
||||||
continue;
|
continue;
|
||||||
@ -613,7 +610,7 @@ static void terminate_agents(Set *pids) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
signum = sigtimedwait(&set, NULL, &ts);
|
signum = sigtimedwait(&set, NULL, TIMESPEC_STORE(50 * USEC_PER_MSEC));
|
||||||
if (signum < 0) {
|
if (signum < 0) {
|
||||||
if (errno != EAGAIN)
|
if (errno != EAGAIN)
|
||||||
log_error_errno(errno, "sigtimedwait() failed: %m");
|
log_error_errno(errno, "sigtimedwait() failed: %m");
|
||||||
|
|||||||
@ -251,7 +251,6 @@ static int start_workers(Manager *m, bool explicit_request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int manager_startup(Manager *m) {
|
int manager_startup(Manager *m) {
|
||||||
struct timeval ts;
|
|
||||||
int n, r;
|
int n, r;
|
||||||
|
|
||||||
assert(m);
|
assert(m);
|
||||||
@ -300,7 +299,7 @@ int manager_startup(Manager *m) {
|
|||||||
|
|
||||||
/* Let's make sure every accept() call on this socket times out after 25s. This allows workers to be
|
/* Let's make sure every accept() call on this socket times out after 25s. This allows workers to be
|
||||||
* GC'ed on idle */
|
* GC'ed on idle */
|
||||||
if (setsockopt(m->listen_fd, SOL_SOCKET, SO_RCVTIMEO, timeval_store(&ts, LISTEN_TIMEOUT_USEC), sizeof(ts)) < 0)
|
if (setsockopt(m->listen_fd, SOL_SOCKET, SO_RCVTIMEO, TIMEVAL_STORE(LISTEN_TIMEOUT_USEC), sizeof(struct timeval)) < 0)
|
||||||
return log_error_errno(errno, "Failed to se SO_RCVTIMEO: %m");
|
return log_error_errno(errno, "Failed to se SO_RCVTIMEO: %m");
|
||||||
|
|
||||||
return start_workers(m, false);
|
return start_workers(m, false);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user