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 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
|
||||
- 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: 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
|
||||
case hexadecimal characters (i.e. without any UUID formatting). This key is
|
||||
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
|
||||
`$BOOT`. It is recommended that every distribution creates a machine id 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
|
||||
title Fedora 19 (Rawhide)
|
||||
version 3.8.0-2.fc19.x86_64
|
||||
sort-key fedora
|
||||
machine-id 6a9857a393724b7a981ebb5b8495b9ea
|
||||
version 3.8.0-2.fc19.x86_64
|
||||
options root=UUID=6d3376e4-fc93-4509-95ec-a21d68011da2
|
||||
architecture x64
|
||||
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
|
||||
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
|
||||
on the `machine-id` and `version` fields, and possibly others. It uses the file
|
||||
name to identify specific items, for example in case it supports storing away
|
||||
default entry information somewhere. A boot loader should generally not modify
|
||||
these files.
|
||||
on the `sort-key`, `machine-id` and `version` fields, and possibly others. It
|
||||
uses the file name to identify specific items, for example in case it supports
|
||||
storing away default entry information somewhere. A boot loader should
|
||||
generally not modify these files.
|
||||
|
||||
For "Boot Loader Specification Entries" (Type #1), the _kernel package
|
||||
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.
|
||||
|
||||
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
|
||||
trouble with UIDs outside of the signed 32bit range, i.e any UIDs equal to or
|
||||
above 2147483648. It is thus strongly recommended to stay away from this range
|
||||
in order to avoid complications. This range should be considered reserved for
|
||||
future, special purposes.
|
||||
with care. Various programs (including kernel file systems — see `devpts` — or
|
||||
even kernel syscalls – see `setfsuid()`) have trouble with UIDs outside of the
|
||||
signed 32bit range, i.e any UIDs equal to or above 2147483648. It is thus
|
||||
strongly recommended to stay away from this range in order to avoid
|
||||
complications. This range should be considered reserved for future, special
|
||||
purposes.
|
||||
|
||||
## 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*:*
|
||||
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
|
||||
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*[tT][xX]2*:*
|
||||
KEYBOARD_KEY_c2=media
|
||||
|
||||
@ -501,10 +501,10 @@
|
||||
considered 'good' from then on.</para>
|
||||
|
||||
<para>The boot menu takes the 'tries left' counter into account when sorting the menu entries: entries in 'bad'
|
||||
state are ordered towards the end of the list, and entries in 'good' or 'indeterminate' towards the beginning.
|
||||
The user can freely choose to boot any entry of the menu, including those already marked 'bad'. If the menu entry
|
||||
to boot is automatically determined, this means that 'good' or 'indeterminate' entries are generally preferred as
|
||||
boot entries are tried in sort order, and 'bad' entries will only be considered if there are no 'good' or
|
||||
state are ordered at the beginning of the list, and entries in 'good' or 'indeterminate' at the end. The user can
|
||||
freely choose to boot any entry of the menu, including those already marked 'bad'. If the menu entry to boot is
|
||||
automatically determined, this means that 'good' or 'indeterminate' entries are generally preferred (as the bottom
|
||||
item of the menu is the one booted by default), and 'bad' entries will only be considered if there are no 'good' or
|
||||
'indeterminate' entries left.</para>
|
||||
|
||||
<para>The <citerefentry><refentrytitle>kernel-install</refentrytitle><manvolnum>8</manvolnum></citerefentry> kernel
|
||||
|
||||
@ -40,6 +40,7 @@ items = [['busctl', ''],
|
||||
['loginctl', 'ENABLE_LOGIND'],
|
||||
['machinectl', 'ENABLE_MACHINED'],
|
||||
['networkctl', 'ENABLE_NETWORKD'],
|
||||
['oomctl', 'ENABLE_OOMD'],
|
||||
['portablectl', 'ENABLE_PORTABLED'],
|
||||
['resolvectl', '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'],
|
||||
['_machinectl', 'ENABLE_MACHINED'],
|
||||
['_networkctl', 'ENABLE_NETWORKD'],
|
||||
['_oomctl', 'ENABLE_OOMD'],
|
||||
['_systemd-inhibit', 'ENABLE_LOGIND'],
|
||||
['_resolvectl', 'ENABLE_RESOLVE'],
|
||||
['_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) {
|
||||
struct timespec ts;
|
||||
int r;
|
||||
|
||||
assert(fds || nfds == 0);
|
||||
@ -167,7 +166,7 @@ int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) {
|
||||
if (nfds == 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)
|
||||
return -errno;
|
||||
if (r == 0)
|
||||
|
||||
@ -820,13 +820,12 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) {
|
||||
for (;;) {
|
||||
usec_t n;
|
||||
siginfo_t status = {};
|
||||
struct timespec ts;
|
||||
|
||||
n = now(CLOCK_MONOTONIC);
|
||||
if (n >= until)
|
||||
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. */
|
||||
if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) {
|
||||
if (status.si_pid == pid) {
|
||||
|
||||
@ -70,10 +70,9 @@ char* strv_find_startswith(char * const *l, const char *name) {
|
||||
}
|
||||
|
||||
char** strv_free(char **l) {
|
||||
if (!l)
|
||||
return NULL;
|
||||
char **k;
|
||||
|
||||
for (char **k = l; *k; k++)
|
||||
STRV_FOREACH(k, l)
|
||||
free(*k);
|
||||
|
||||
return mfree(l);
|
||||
@ -89,32 +88,30 @@ char** strv_free_erase(char **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);
|
||||
if (!r)
|
||||
result = new(char*, strv_length(l) + 1);
|
||||
if (!result)
|
||||
return NULL;
|
||||
|
||||
if (l)
|
||||
for (; *l; k++, l++) {
|
||||
*k = strdup(*l);
|
||||
if (!*k) {
|
||||
strv_free(r);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
k = result;
|
||||
STRV_FOREACH(i, l) {
|
||||
*k = strdup(*i);
|
||||
if (!*k)
|
||||
return NULL;
|
||||
k++;
|
||||
}
|
||||
|
||||
*k = NULL;
|
||||
return r;
|
||||
return TAKE_PTR(result);
|
||||
}
|
||||
|
||||
size_t strv_length(char * const *l) {
|
||||
char * const *i;
|
||||
size_t n = 0;
|
||||
|
||||
if (!l)
|
||||
return 0;
|
||||
|
||||
for (; *l; l++)
|
||||
STRV_FOREACH(i, l)
|
||||
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_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_;
|
||||
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_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_;
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include "efi-loader.h"
|
||||
#include "efivars.h"
|
||||
#include "fd-util.h"
|
||||
#include "find-esp.h"
|
||||
#include "fs-util.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "find-esp.h"
|
||||
#include "fs-util.h"
|
||||
#include "glyph-util.h"
|
||||
#include "main-func.h"
|
||||
@ -590,6 +591,8 @@ static int boot_entry_show(
|
||||
|
||||
printf(" source: %s\n", link ?: e->path);
|
||||
}
|
||||
if (e->sort_key)
|
||||
printf(" sort-key: %s\n", e->sort_key);
|
||||
if (e->version)
|
||||
printf(" version: %s\n", e->version);
|
||||
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 *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 *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 *machine_id;
|
||||
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) {
|
||||
assert(fmt);
|
||||
if (value)
|
||||
@ -443,7 +455,11 @@ static BOOLEAN ps_continue(void) {
|
||||
UINT64 key;
|
||||
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);
|
||||
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" 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" sort key: %s\n", entry->sort_key);
|
||||
ps_string(L" version: %s\n", entry->version);
|
||||
ps_string(L" machine-id: %s\n", entry->machine_id);
|
||||
if (entry->device)
|
||||
@ -598,7 +615,7 @@ static BOOLEAN menu_run(
|
||||
UINTN x_start = 0, y_start = 0, y_status = 0;
|
||||
UINTN x_max, y_max;
|
||||
_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_remain = config->timeout_sec == TIMEOUT_MENU_FORCE ? 0 : config->timeout_sec;
|
||||
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);
|
||||
}
|
||||
|
||||
UINTN line_width = 0, entry_padding = 3;
|
||||
while (!exit) {
|
||||
UINT64 key;
|
||||
|
||||
if (new_mode) {
|
||||
UINTN line_width = 0, entry_padding = 3;
|
||||
|
||||
console_query_mode(&x_max, &y_max);
|
||||
|
||||
/* account for padding+status */
|
||||
@ -643,6 +659,7 @@ static BOOLEAN menu_run(
|
||||
idx_last = idx_first + visible_max - 1;
|
||||
|
||||
/* length of the longest entry */
|
||||
line_width = 0;
|
||||
for (UINTN i = 0; i < config->entry_count; i++)
|
||||
line_width = MAX(line_width, StrLen(config->entries[i]->title_show));
|
||||
line_width = MIN(line_width + 2 * entry_padding, x_max);
|
||||
@ -655,10 +672,11 @@ static BOOLEAN menu_run(
|
||||
y_start = 0;
|
||||
|
||||
/* 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);
|
||||
clearline = mfree(clearline);
|
||||
separator = mfree(separator);
|
||||
|
||||
/* menu entries title lines */
|
||||
lines = xnew(CHAR16*, config->entry_count + 1);
|
||||
@ -682,9 +700,13 @@ static BOOLEAN menu_run(
|
||||
lines[config->entry_count] = NULL;
|
||||
|
||||
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] = ' ';
|
||||
separator[i] = unicode_supported() ? L'─' : L'-';
|
||||
}
|
||||
clearline[x_max] = 0;
|
||||
separator[x_max] = 0;
|
||||
|
||||
new_mode = FALSE;
|
||||
clear = TRUE;
|
||||
@ -704,16 +726,16 @@ static BOOLEAN menu_run(
|
||||
if (i == config->idx_default_efivar)
|
||||
print_at(x_start, y_start + i - idx_first,
|
||||
(i == idx_highlight) ? COLOR_HIGHLIGHT : COLOR_ENTRY,
|
||||
(CHAR16*) L"=>");
|
||||
(CHAR16*) (unicode_supported() ? L" ►" : L"=>"));
|
||||
}
|
||||
refresh = FALSE;
|
||||
} 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 - idx_first, COLOR_HIGHLIGHT, lines[idx_highlight]);
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -722,20 +744,24 @@ static BOOLEAN menu_run(
|
||||
status = xpool_print(L"Boot in %u s.", timeout_remain);
|
||||
}
|
||||
|
||||
/* print status at last line of screen */
|
||||
if (status) {
|
||||
UINTN len;
|
||||
UINTN x;
|
||||
|
||||
/* center line */
|
||||
len = StrLen(status);
|
||||
if (len < x_max)
|
||||
x = (x_max - len) / 2;
|
||||
else
|
||||
x = 0;
|
||||
print_at(0, y_status, COLOR_NORMAL, clearline + (x_max - x));
|
||||
/* If we draw the last char of the last line, the screen will scroll and break our
|
||||
* 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
|
||||
* on the last line. */
|
||||
UINTN len = StrnLen(status, x_max - 1);
|
||||
UINTN x = (x_max - len) / 2;
|
||||
status[len] = '\0';
|
||||
print_at(0, y_status, COLOR_NORMAL, clearline + x_max - x);
|
||||
ST->ConOut->OutputString(ST->ConOut, status);
|
||||
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. */
|
||||
@ -766,11 +792,7 @@ static BOOLEAN menu_run(
|
||||
timeout_remain = 0;
|
||||
|
||||
/* clear status after keystroke */
|
||||
if (status) {
|
||||
FreePool(status);
|
||||
status = NULL;
|
||||
print_at(0, y_status, COLOR_NORMAL, clearline + 1);
|
||||
}
|
||||
status = mfree(status);
|
||||
|
||||
idx_highlight_prev = idx_highlight;
|
||||
|
||||
@ -1026,6 +1048,7 @@ static void config_entry_free(ConfigEntry *entry) {
|
||||
FreePool(entry->id);
|
||||
FreePool(entry->title_show);
|
||||
FreePool(entry->title);
|
||||
FreePool(entry->sort_key);
|
||||
FreePool(entry->version);
|
||||
FreePool(entry->machine_id);
|
||||
FreePool(entry->loader);
|
||||
@ -1427,6 +1450,12 @@ static void config_entry_add_from_file(
|
||||
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) {
|
||||
FreePool(entry->version);
|
||||
entry->version = xstra_to_str(value);
|
||||
@ -1531,6 +1560,7 @@ static void config_entry_add_from_file(
|
||||
|
||||
entry->device = device;
|
||||
entry->id = xstrdup(file);
|
||||
StrLwr(entry->id);
|
||||
|
||||
config_add_entry(config, entry);
|
||||
|
||||
@ -1609,6 +1639,8 @@ static void config_load_entries(
|
||||
assert(device);
|
||||
assert(root_dir);
|
||||
|
||||
/* Adds Boot Loader Type #1 entries (i.e. /loader/entries/….conf) */
|
||||
|
||||
err = open_directory(root_dir, L"\\loader\\entries", &entries_dir);
|
||||
if (EFI_ERROR(err))
|
||||
return;
|
||||
@ -1642,24 +1674,40 @@ static INTN config_entry_compare(const ConfigEntry *a, const ConfigEntry *b) {
|
||||
assert(a);
|
||||
assert(b);
|
||||
|
||||
/* Order entries that have no tries left towards the end of the list. They have
|
||||
* proven to be bad and should not be selected automatically. */
|
||||
if (a->tries_left != 0 && b->tries_left == 0)
|
||||
return -1;
|
||||
/* Order entries that have no tries left to the end of the list */
|
||||
if (a->tries_left == 0 && b->tries_left != 0)
|
||||
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 (r != 0)
|
||||
/* If there's a sort key defined for *both* entries, then we do new-style ordering, i.e. by
|
||||
* 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;
|
||||
if (a->sort_key && b->sort_key) {
|
||||
|
||||
/* Sort by machine id now so that different installations don't interleave their versions. */
|
||||
r = strcasecmp_ptr(a->machine_id, b->machine_id);
|
||||
if (r != 0)
|
||||
return r;
|
||||
r = strcmp(a->sort_key, b->sort_key);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
/* Reverse version comparison order so that higher versions are preferred. */
|
||||
r = strverscmp_improved(b->version, a->version);
|
||||
/* If multiple installations of the same OS are around, group by machine ID */
|
||||
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)
|
||||
return r;
|
||||
|
||||
@ -1668,19 +1716,18 @@ static INTN config_entry_compare(const ConfigEntry *a, const ConfigEntry *b) {
|
||||
return 0;
|
||||
|
||||
/* 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)
|
||||
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 (a->tries_done < b->tries_done)
|
||||
return -1;
|
||||
if (a->tries_done > b->tries_done)
|
||||
return 1;
|
||||
if (a->tries_done < b->tries_done)
|
||||
return -1;
|
||||
|
||||
/* As a last resort, use the id (file name). */
|
||||
return strverscmp_improved(a->id, b->id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static UINTN config_entry_find(Config *config, const CHAR16 *needle) {
|
||||
@ -1724,7 +1771,7 @@ static void config_default_entry_select(Config *config) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Select the first suitable entry. */
|
||||
/* select the first suitable entry */
|
||||
for (i = 0; i < config->entry_count; i++) {
|
||||
if (config->entries[i]->type == LOADER_AUTO || config->entries[i]->call)
|
||||
continue;
|
||||
@ -1853,6 +1900,7 @@ static ConfigEntry *config_entry_add_loader(
|
||||
CHAR16 key,
|
||||
const CHAR16 *title,
|
||||
const CHAR16 *loader,
|
||||
const CHAR16 *sort_key,
|
||||
const CHAR16 *version) {
|
||||
|
||||
ConfigEntry *entry;
|
||||
@ -1871,11 +1919,14 @@ static ConfigEntry *config_entry_add_loader(
|
||||
.device = device,
|
||||
.loader = xstrdup(loader),
|
||||
.id = xstrdup(id),
|
||||
.sort_key = xstrdup(sort_key),
|
||||
.key = key,
|
||||
.tries_done = UINTN_MAX,
|
||||
.tries_left = UINTN_MAX,
|
||||
};
|
||||
|
||||
StrLwr(entry->id);
|
||||
|
||||
config_add_entry(config, entry);
|
||||
return entry;
|
||||
}
|
||||
@ -1943,7 +1994,7 @@ static ConfigEntry *config_entry_add_loader_auto(
|
||||
if (EFI_ERROR(err))
|
||||
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) {
|
||||
@ -2097,6 +2148,8 @@ static void config_entry_add_linux(
|
||||
UINTN f_size = 0;
|
||||
EFI_STATUS err;
|
||||
|
||||
/* Adds Boot Loader Type #2 entries (i.e. /EFI/Linux/….efi) */
|
||||
|
||||
assert(config);
|
||||
assert(device);
|
||||
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,
|
||||
*os_image_version = NULL, *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL,
|
||||
*path = NULL;
|
||||
const CHAR16 *good_name, *good_version;
|
||||
const CHAR16 *good_name, *good_version, *good_sort_key;
|
||||
_cleanup_freepool_ CHAR8 *content = NULL;
|
||||
UINTN offs[_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_image_id,
|
||||
os_name,
|
||||
@ -2212,7 +2265,8 @@ static void config_entry_add_linux(
|
||||
os_version_id,
|
||||
os_build_id,
|
||||
&good_name,
|
||||
&good_version))
|
||||
&good_version,
|
||||
&good_sort_key))
|
||||
continue;
|
||||
|
||||
path = xpool_print(L"\\EFI\\Linux\\%s", f->FileName);
|
||||
@ -2220,10 +2274,11 @@ static void config_entry_add_linux(
|
||||
config,
|
||||
device,
|
||||
LOADER_UNIFIED_LINUX,
|
||||
f->FileName,
|
||||
/* id= */ f->FileName,
|
||||
/* key= */ 'l',
|
||||
good_name,
|
||||
path,
|
||||
/* title= */ good_name,
|
||||
/* loader= */ path,
|
||||
/* sort_key= */ good_sort_key,
|
||||
good_version);
|
||||
|
||||
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 */
|
||||
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_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 */
|
||||
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,
|
||||
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,
|
||||
|
||||
@ -1571,8 +1571,6 @@ static void initialize_clock(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
|
||||
* command line and such. */
|
||||
|
||||
@ -1582,7 +1580,7 @@ static void apply_clock_update(void) {
|
||||
if (getpid_cached() != 1)
|
||||
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");
|
||||
else
|
||||
log_info("Set system clock to %s, as specified on the kernel command line.",
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
#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_image_id,
|
||||
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_build_id,
|
||||
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)
|
||||
* fields). Precise is preferred over vague, and human readable over machine readable. Thus:
|
||||
/* Find the best human readable title, version string and sort key for a boot entry (using the
|
||||
* 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,
|
||||
* 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.
|
||||
*
|
||||
* 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"
|
||||
* string (i.e. not on os-release(5) data). */
|
||||
* sorting (i.e. algorithmic ordering of boot entries) is done based on the order of the sort key (if
|
||||
* defined) or entry "id" string (i.e. entry file name) otherwise. */
|
||||
|
||||
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_sort_key = os_image_id ?: os_id;
|
||||
|
||||
if (!good_name || !good_version)
|
||||
return sd_false;
|
||||
@ -52,5 +55,8 @@ sd_bool bootspec_pick_name_version(
|
||||
if (ret_version)
|
||||
*ret_version = good_version;
|
||||
|
||||
if (ret_sort_key)
|
||||
*ret_sort_key = good_sort_key;
|
||||
|
||||
return sd_true;
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
|
||||
#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_image_id,
|
||||
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_build_id,
|
||||
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"
|
||||
|
||||
SORT_KEY="$IMAGE_ID"
|
||||
[ -z "$SORT_KEY" ] && SORT_KEY="$ID"
|
||||
|
||||
if [ -r /etc/kernel/cmdline ]; then
|
||||
BOOT_OPTIONS="$(tr -s "$IFS" ' ' </etc/kernel/cmdline)"
|
||||
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
|
||||
echo "machine-id $MACHINE_ID"
|
||||
fi
|
||||
[ -n "$SORT_KEY" ] && echo "sort-key $SORT_KEY"
|
||||
echo "options $BOOT_OPTIONS"
|
||||
echo "linux $ENTRY_DIR/linux"
|
||||
|
||||
|
||||
@ -3906,6 +3906,7 @@ static int epoll_wait_usec(
|
||||
int msec;
|
||||
#if 0
|
||||
static bool epoll_pwait2_absent = false;
|
||||
int r;
|
||||
|
||||
/* 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. */
|
||||
|
||||
if (!epoll_pwait2_absent && timeout != USEC_INFINITY) {
|
||||
struct timespec ts;
|
||||
|
||||
r = epoll_pwait2(fd,
|
||||
events,
|
||||
maxevents,
|
||||
timespec_store(&ts, timeout),
|
||||
TIMESPEC_STORE(timeout),
|
||||
NULL);
|
||||
if (r >= 0)
|
||||
return r;
|
||||
|
||||
@ -142,10 +142,8 @@ int stub_pid1(sd_id128_t uuid) {
|
||||
|
||||
if (quit_usec == USEC_INFINITY)
|
||||
r = sigwaitinfo(&waitmask, &si);
|
||||
else {
|
||||
struct timespec ts;
|
||||
r = sigtimedwait(&waitmask, &si, timespec_store(&ts, quit_usec - current_usec));
|
||||
}
|
||||
else
|
||||
r = sigtimedwait(&waitmask, &si, TIMESPEC_STORE(quit_usec - current_usec));
|
||||
if (r < 0) {
|
||||
if (errno == EINTR) /* strace -p attach can result in EINTR, let's handle this nicely. */
|
||||
continue;
|
||||
|
||||
@ -1,40 +1,22 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <linux/magic.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-fundamental.h"
|
||||
#include "conf-files.h"
|
||||
#include "def.h"
|
||||
#include "device-nodes.h"
|
||||
#include "dirent-util.h"
|
||||
#include "efivars.h"
|
||||
#include "efi-loader.h"
|
||||
#include "env-file.h"
|
||||
#include "env-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "gpt.h"
|
||||
#include "id128-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "find-esp.h"
|
||||
#include "path-util.h"
|
||||
#include "pe-header.h"
|
||||
#include "sort-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "unaligned.h"
|
||||
#include "util.h"
|
||||
#include "virt.h"
|
||||
|
||||
static void boot_entry_free(BootEntry *entry) {
|
||||
assert(entry);
|
||||
@ -45,6 +27,7 @@ static void boot_entry_free(BootEntry *entry) {
|
||||
free(entry->root);
|
||||
free(entry->title);
|
||||
free(entry->show_title);
|
||||
free(entry->sort_key);
|
||||
free(entry->version);
|
||||
free(entry->machine_id);
|
||||
free(entry->architecture);
|
||||
@ -131,6 +114,8 @@ static int boot_entry_load(
|
||||
|
||||
if (streq(field, "title"))
|
||||
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"))
|
||||
r = free_and_strdup(&tmp.version, p);
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -311,7 +318,7 @@ static int boot_entry_load_unified(
|
||||
_cleanup_(boot_entry_free) BootEntry tmp = {
|
||||
.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;
|
||||
int r;
|
||||
|
||||
@ -339,7 +346,7 @@ static int boot_entry_load_unified(
|
||||
if (r < 0)
|
||||
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_image_id,
|
||||
os_name,
|
||||
@ -349,7 +356,8 @@ static int boot_entry_load_unified(
|
||||
os_version_id,
|
||||
os_build_id,
|
||||
&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);
|
||||
|
||||
r = path_extract_filename(path, &tmp.id);
|
||||
@ -387,6 +395,10 @@ static int boot_entry_load_unified(
|
||||
if (!tmp.title)
|
||||
return log_oom();
|
||||
|
||||
tmp.sort_key = strdup(good_sort_key);
|
||||
if (!tmp.sort_key)
|
||||
return log_oom();
|
||||
|
||||
tmp.version = strdup(good_version);
|
||||
if (!tmp.version)
|
||||
return log_oom();
|
||||
@ -634,6 +646,19 @@ static int boot_entries_uniquify(BootEntry *entries, size_t n_entries) {
|
||||
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) {
|
||||
int i;
|
||||
|
||||
@ -645,47 +670,45 @@ static int boot_entries_select_default(const BootConfig *config) {
|
||||
return -1; /* -1 means "no default" */
|
||||
}
|
||||
|
||||
if (config->entry_oneshot)
|
||||
for (i = config->n_entries - 1; i >= 0; i--)
|
||||
if (streq(config->entry_oneshot, config->entries[i].id)) {
|
||||
log_debug("Found default: id \"%s\" is matched by LoaderEntryOneShot",
|
||||
config->entries[i].id);
|
||||
return i;
|
||||
}
|
||||
if (config->entry_oneshot) {
|
||||
i = boot_config_find(config, config->entry_oneshot);
|
||||
if (i >= 0) {
|
||||
log_debug("Found default: id \"%s\" is matched by LoaderEntryOneShot",
|
||||
config->entries[i].id);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
if (config->entry_default)
|
||||
for (i = config->n_entries - 1; i >= 0; i--)
|
||||
if (streq(config->entry_default, config->entries[i].id)) {
|
||||
log_debug("Found default: id \"%s\" is matched by LoaderEntryDefault",
|
||||
config->entries[i].id);
|
||||
return i;
|
||||
}
|
||||
if (config->entry_default) {
|
||||
i = boot_config_find(config, config->entry_default);
|
||||
if (i >= 0) {
|
||||
log_debug("Found default: id \"%s\" is matched by LoaderEntryDefault",
|
||||
config->entries[i].id);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
if (config->default_pattern)
|
||||
for (i = config->n_entries - 1; i >= 0; i--)
|
||||
if (fnmatch(config->default_pattern, config->entries[i].id, FNM_CASEFOLD) == 0) {
|
||||
log_debug("Found default: id \"%s\" is matched by pattern \"%s\"",
|
||||
config->entries[i].id, config->default_pattern);
|
||||
return i;
|
||||
}
|
||||
if (config->default_pattern) {
|
||||
i = boot_config_find(config, config->default_pattern);
|
||||
if (i >= 0) {
|
||||
log_debug("Found default: id \"%s\" is matched by pattern \"%s\"",
|
||||
config->entries[i].id, config->default_pattern);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
log_debug("Found default: last entry \"%s\"", config->entries[config->n_entries - 1].id);
|
||||
return config->n_entries - 1;
|
||||
log_debug("Found default: first entry \"%s\"", config->entries[0].id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int boot_entries_select_selected(const BootConfig *config) {
|
||||
|
||||
assert(config);
|
||||
assert(config->entries || config->n_entries == 0);
|
||||
|
||||
if (!config->entry_selected || config->n_entries == 0)
|
||||
return -1;
|
||||
|
||||
for (int i = config->n_entries - 1; i >= 0; i--)
|
||||
if (streq(config->entry_selected, config->entries[i].id))
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
return boot_config_find(config, config->entry_selected);
|
||||
}
|
||||
|
||||
static int boot_load_efi_entry_pointers(BootConfig *config) {
|
||||
@ -884,696 +907,3 @@ int boot_entries_augment_from_loader(
|
||||
|
||||
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
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "sd-id128.h"
|
||||
|
||||
#include "string-util.h"
|
||||
|
||||
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 *title;
|
||||
char *show_title;
|
||||
char *sort_key;
|
||||
char *version;
|
||||
char *machine_id;
|
||||
char *architecture;
|
||||
@ -90,6 +90,3 @@ static inline const char* boot_entry_title(const BootEntry *entry) {
|
||||
|
||||
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"
|
||||
|
||||
int clock_apply_epoch(ClockChangeDirection *ret_attempted_change) {
|
||||
struct stat st;
|
||||
struct timespec ts;
|
||||
usec_t epoch_usec, now_usec;
|
||||
struct stat st;
|
||||
|
||||
/* NB: we update *ret_attempted_change in *all* cases, both
|
||||
* 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;
|
||||
}
|
||||
|
||||
if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, epoch_usec)) < 0)
|
||||
if (clock_settime(CLOCK_REALTIME, TIMESPEC_STORE(epoch_usec)) < 0)
|
||||
return -errno;
|
||||
|
||||
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',
|
||||
'fileio-label.c',
|
||||
'fileio-label.h',
|
||||
'find-esp.c',
|
||||
'find-esp.h',
|
||||
'firewall-util-nft.c',
|
||||
'firewall-util-private.h',
|
||||
'firewall-util.c',
|
||||
@ -176,8 +178,8 @@ shared_sources = files(
|
||||
'json.h',
|
||||
'kbd-util.c',
|
||||
'kbd-util.h',
|
||||
'keyring-util.h',
|
||||
'keyring-util.c',
|
||||
'keyring-util.h',
|
||||
'killall.c',
|
||||
'killall.h',
|
||||
'label.c',
|
||||
|
||||
@ -513,6 +513,8 @@ tests += [
|
||||
|
||||
[files('test-strbuf.c')],
|
||||
|
||||
[files('test-bootspec.c')],
|
||||
|
||||
[files('test-strv.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:
|
||||
ct = now(CLOCK_REALTIME);
|
||||
if (ct < min) {
|
||||
struct timespec ts;
|
||||
char date[FORMAT_TIMESTAMP_MAX];
|
||||
|
||||
log_info("System clock time unset or jumped backwards, restoring from recorded timestamp: %s",
|
||||
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");
|
||||
}
|
||||
|
||||
|
||||
@ -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) {
|
||||
struct timespec ts;
|
||||
siginfo_t status = {};
|
||||
sigset_t set;
|
||||
void *p;
|
||||
int r, signum;
|
||||
@ -599,11 +597,10 @@ static void terminate_agents(Set *pids) {
|
||||
*/
|
||||
assert_se(sigemptyset(&set) >= 0);
|
||||
assert_se(sigaddset(&set, SIGCHLD) >= 0);
|
||||
timespec_store(&ts, 50 * USEC_PER_MSEC);
|
||||
|
||||
while (!set_isempty(pids)) {
|
||||
siginfo_t status = {};
|
||||
|
||||
zero(status);
|
||||
r = waitid(P_ALL, 0, &status, WEXITED|WNOHANG);
|
||||
if (r < 0 && errno == EINTR)
|
||||
continue;
|
||||
@ -613,7 +610,7 @@ static void terminate_agents(Set *pids) {
|
||||
continue;
|
||||
}
|
||||
|
||||
signum = sigtimedwait(&set, NULL, &ts);
|
||||
signum = sigtimedwait(&set, NULL, TIMESPEC_STORE(50 * USEC_PER_MSEC));
|
||||
if (signum < 0) {
|
||||
if (errno != EAGAIN)
|
||||
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) {
|
||||
struct timeval ts;
|
||||
int n, r;
|
||||
|
||||
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
|
||||
* 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 start_workers(m, false);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user