1
0
mirror of https://github.com/systemd/systemd synced 2026-03-25 08:14:54 +01:00

Compare commits

..

23 Commits

Author SHA1 Message Date
Yu Watanabe
8908ceb7a8
Merge pull request #20432 from yuwata/network-recreate-stacked-netdevs
network: recreate stacked netdevs when underlying device is re-added
2021-08-13 17:31:28 +09:00
Jan Janssen
702d40270a meson: Make unused-function an error 2021-08-13 09:23:45 +02:00
Steven Siloti
8a33aa199d resolved: retry on SERVFAIL before downgrading feature level
The SERVFAIL RCODE can be generated for many reasons which may not be related
to lack of feature support. For example, the Stubby resolver generates
SERVFAIL when a request times out. Such transient failures can cause
unnecessary downgrades to both the transaction and the server's feature level.
The consequences of this are especially severe if the server is in DNSSEC
strict mode. In this case repeated downgrades eventually cause the server to
stop resolving entirely with the error "incompatible-server".

To avoid unnecessary downgrades the request should be retried once with the
current level before the transaction's feature level is downgraded.
2021-08-13 09:23:12 +02:00
Lennart Poettering
d8151fb949
Merge pull request #20233 from maanyagoenka/log-error
systemd-analyze: add option to return an error value when unit verification fails
2021-08-13 09:22:48 +02:00
Lennart Poettering
2c3735d6ba
Merge pull request #20350 from medhefgo/boot
Grab bag of sd-boot improvements
2021-08-13 09:22:12 +02:00
Maanya Goenka
3cc3dc7736 systemd-analyze: option to exit with an error when 'verify' fails
The commit introduces a callback invoked from log_syntax_internal.
Use it from systemd-analyze to gather a list of units that contain
syntax warnings. A new command line option is added to make use of this.

The new option --recursive-errors takes in three possible modes:

1. yes - which is the default. systemd-analyze exits with an error when syntax warnings arise during verification of the
	 specified units or any of their dependencies.
3. no - systemd-analyze exits with an error when syntax warnings arise during verification of only the selected unit.
	Analyzing and loading any dependencies will be skipped.
4. one - systemd-analyze exits with an error when syntax warnings arise during verification
	 of only the selected units and their direct dependencies.

Below are two service unit files that I created for the purposes of testing:

1. First, we run the commands on a unit that does not have dependencies but has a non-existing key-value setting (i.e. foo = bar).

> cat <<EOF>testcase.service

[Unit]
foo = bar

[Service]
ExecStart = echo hello
EOF

OUTPUT:

maanya-goenka@debian:~/systemd (log-error)$ sudo build/systemd-analyze verify testcase.service
/home/maanya-goenka/systemd/testcase.service:2: Unknown key name 'foo' in section 'Unit', ignoring.
/usr/lib/systemd/system/plymouth-start.service:15: Unit configured to use KillMode=none. This is unsafe, as it disables systemd's process lifecycle management for the service. Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. Support for KillMode=none is deprecated and will eventually be removed.
/usr/lib/systemd/system/dbus.socket:5: ListenStream= references a path below legacy directory /var/run/, updating /var/run/dbus/system_bus_socket → /run/dbus/system_bus_socket; please update the unit file accordingly.
/usr/lib/systemd/system/gdm.service:30: Standard output type syslog is obsolete, automatically updating to journal. Please update your unit file, and consider removing the setting altogether.
maanya-goenka@debian:~/systemd (log-error)$ echo $?
1

maanya-goenka@debian:~/systemd (log-error)$ sudo build/systemd-analyze verify --recursive-errors=yes testcase.service
/home/maanya-goenka/systemd/testcase.service:2: Unknown key name 'foo' in section 'Unit', ignoring.
/usr/lib/systemd/system/plymouth-start.service:15: Unit configured to use KillMode=none. This is unsafe, as it disables systemd's process lifecycle management for the service. Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. Support for KillMode=none is deprecated and will eventually be removed.
/usr/lib/systemd/system/dbus.socket:5: ListenStream= references a path below legacy directory /var/run/, updating /var/run/dbus/system_bus_socket → /run/dbus/system_bus_socket; please update the unit file accordingly.
/usr/lib/systemd/system/gdm.service:30: Standard output type syslog is obsolete, automatically updating to journal. Please update your unit file, and consider removing the setting altogether.
maanya-goenka@debian:~/systemd (log-error)$ echo $?
1

maanya-goenka@debian:~/systemd (log-error)$ sudo build/systemd-analyze verify --recursive-errors=no testcase.service
/home/maanya-goenka/systemd/testcase.service:2: Unknown key name 'foo' in section 'Unit', ignoring.
maanya-goenka@debian:~/systemd (log-error)$ echo $?
1

maanya-goenka@debian:~/systemd (log-error)$ sudo build/systemd-analyze verify --recursive-errors=one testcase.service
/home/maanya-goenka/systemd/testcase.service:2: Unknown key name 'foo' in section 'Unit', ignoring.
/usr/lib/systemd/system/plymouth-start.service:15: Unit configured to use KillMode=none. This is unsafe, as it disables systemd's process lifecycle management for the service. Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. Support for KillMode=none is deprecated and will eventually be removed.
/usr/lib/systemd/system/dbus.socket:5: ListenStream= references a path below legacy directory /var/run/, updating /var/run/dbus/system_bus_socket → /run/dbus/system_bus_socket; please update the unit file accordingly.
/usr/lib/systemd/system/gdm.service:30: Standard output type syslog is obsolete, automatically updating to journal. Please update your unit file, and consider removing the setting altogether.
maanya-goenka@debian:~/systemd (log-error)$ echo $?
1

2. Next, we run the commands on a unit that is syntactically valid but has a non-existing dependency (i.e. foo2.service)

> cat <<EOF>foobar.service

[Unit]
Requires = foo2.service

[Service]
ExecStart = echo hello
EOF

OUTPUT:

maanya-goenka@debian:~/systemd (log-error)$ sudo build/systemd-analyze verify foobar.service
/usr/lib/systemd/system/plymouth-start.service:15: Unit configured to use KillMode=none. This is unsafe, as it disables systemd's process lifecycle management for the service. Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. Support for KillMode=none is deprecated and will eventually be removed.
/usr/lib/systemd/system/dbus.socket:5: ListenStream= references a path below legacy directory /var/run/, updating /var/run/dbus/system_bus_socket → /run/dbus/system_bus_socket; please update the unit file accordingly.
/usr/lib/systemd/system/gdm.service:30: Standard output type syslog is obsolete, automatically updating to journal. Please update your unit file, and consider removing the setting altogether.
foobar.service: Failed to create foobar.service/start: Unit foo2.service not found.
maanya-goenka@debian:~/systemd (log-error)$ echo $?
1

maanya-goenka@debian:~/systemd (log-error)$ sudo build/systemd-analyze verify --recursive-errors=yes foobar.service
/usr/lib/systemd/system/plymouth-start.service:15: Unit configured to use KillMode=none. This is unsafe, as it disables systemd's process lifecycle management for the service. Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. Support for KillMode=none is deprecated and will eventually be removed.
/usr/lib/systemd/system/dbus.socket:5: ListenStream= references a path below legacy directory /var/run/, updating /var/run/dbus/system_bus_socket → /run/dbus/system_bus_socket; please update the unit file accordingly.
/usr/lib/systemd/system/gdm.service:30: Standard output type syslog is obsolete, automatically updating to journal. Please update your unit file, and consider removing the setting altogether.
foobar.service: Failed to create foobar.service/start: Unit foo2.service not found.
maanya-goenka@debian:~/systemd (log-error)$ echo $?
1

maanya-goenka@debian:~/systemd (log-error)$ sudo build/systemd-analyze verify --recursive-errors=no foobar.service
maanya-goenka@debian:~/systemd (log-error)$ echo $?
0

maanya-goenka@debian:~/systemd (log-error)$ sudo build/systemd-analyze verify --recursive-errors=one foobar.service
/usr/lib/systemd/system/plymouth-start.service:15: Unit configured to use KillMode=none. This is unsafe, as it disables systemd's process lifecycle management for the service. Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. Support for KillMode=none is deprecated and will eventually be removed.
/usr/lib/systemd/system/dbus.socket:5: ListenStream= references a path below legacy directory /var/run/, updating /var/run/dbus/system_bus_socket → /run/dbus/system_bus_socket; please update the unit file accordingly.
/usr/lib/systemd/system/gdm.service:30: Standard output type syslog is obsolete, automatically updating to journal. Please update your unit file, and consider removing the setting altogether.
foobar.service: Failed to create foobar.service/start: Unit foo2.service not found.
maanya-goenka@debian:~/systemd (log-error)$ echo $?
1
2021-08-12 07:22:15 -07:00
Maanya Goenka
f14d6810e0 manager: add a test flag to ignore dependencies
The MANAGER_TEST_RUN_IGNORE_DEPENDENCIES flag was added in order to allow the caller
to skip the recursive loading of dependency units when loading specific
unit files. This includes the default dependencies, the specified dependencies, the slice.
This will be used by systemd-analyze to allow checking individual unit files in isolation.
2021-08-12 07:22:15 -07:00
Maanya Goenka
1545051c79 manager: use FLAGS_SET when checking for MANAGER_TEST_RUN_MINIMAL
Allows multiple flags to be set, for example, in systemd-analyze.
2021-08-12 07:22:15 -07:00
Jan Janssen
dba0c9832b sd-boot: Allow on/off and t/f for booleans too 2021-08-12 16:10:06 +02:00
Jan Janssen
ec97e40c29 sd-boot: Provide error messages when parsing a config option fails 2021-08-12 16:10:06 +02:00
Jan Janssen
e98d271e57 sd-boot: Rework console input handling
Fixes: #15847
Probably fixes: #19191
2021-08-12 16:10:02 +02:00
Jan Janssen
b3fc3a3ced sd-boot: Use StrSize where it makes sense 2021-08-12 09:48:37 +02:00
Jan Janssen
508df915b6 sd-boot: Assert all the things! 2021-08-12 09:48:37 +02:00
Jan Janssen
7a7267bf55 sd-boot: Add assert implementation
There is a ASSERT() macro from gnu-efi, but that does not show any
output to ConOut. Having to do some additional setup just to get
some debug output is tedious and outright difficult on real hardware.
2021-08-12 09:48:37 +02:00
Jan Janssen
b52fafb26d sd-boot: Fix possible null pointer dereference
Auto entries are showing garbage for the version in print_status()
because StrDuplicate does not expect null pointers.
2021-08-12 09:48:37 +02:00
Jan Janssen
b2cf9922a4 sd-boot: Don't use magic integer constants 2021-08-12 09:48:37 +02:00
Jan Janssen
8aba0eec49 sd-boot: Unify error handling
log_error_stall() and log_error_status_stall() will ensure the user has
a chance to catch an error message by stalling and also forcing a
lightred/black color on it. Also, convert several Print() calls to it
since they are actually error messages.
2021-08-12 09:48:27 +02:00
Yu Watanabe
1d0c9bd753 test-network: add a testcase for recreating stacked netdevs 2021-08-12 15:03:50 +09:00
Yu Watanabe
2f117922d4 network: recreate stacked netdevs when underlying device is re-added
Closes #20430.
2021-08-12 15:03:47 +09:00
Yu Watanabe
b14686ff3a network: adjust log messages, function names, etc. 2021-08-12 14:39:39 +09:00
Yu Watanabe
8f65304c5d network: use netdev_enter_failed() instead of netdev_drop() on error
Preparation for later commits to support reconfiguring netdevs.
2021-08-12 14:23:57 +09:00
Yu Watanabe
798e5dc8ae network: introduce a helper function netdev_is_stacked_and_independent() 2021-08-12 14:10:45 +09:00
Jan Janssen
f862e84724 macro: Move some macros to macro-fundamental.h
Also, make sure STRLEN works with wide strings too.
2021-08-11 14:59:46 +02:00
46 changed files with 876 additions and 424 deletions

View File

@ -41,8 +41,8 @@
a comment line. Empty and comment lines are ignored.</para>
<para>Boolean arguments may be written as
<literal>yes</literal>/<literal>y</literal>/<literal>true</literal>/<literal>1</literal> or
<literal>no</literal>/<literal>n</literal>/<literal>false</literal>/<literal>0</literal>.
<literal>yes</literal>/<literal>y</literal>/<literal>true</literal>/<literal>t</literal>/<literal>on</literal>/<literal>1</literal> or
<literal>no</literal>/<literal>n</literal>/<literal>false</literal>/<literal>f</literal>/<literal>off</literal>/<literal>0</literal>.
</para>
</refsect1>

View File

@ -744,6 +744,18 @@ Service b@0.service not loaded, b.socket cannot be started.
generators enabled will generally result in some warnings.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--recursive-errors=<replaceable>MODE</replaceable></option></term>
<listitem><para>Control verification of units and their dependencies and whether
<command>systemd-analyze verify</command> exits with a non-zero process exit status or not. With
<command>yes</command>, return a non-zero process exit status when warnings arise during verification
of either the specified unit or any of its associated dependencies. This is the default. With
<command>no</command>, return a non-zero process exit status when warnings arise during verification
of only the specified unit. With <command>one</command>, return a non-zero process exit status when
warnings arise during verification of either the specified unit or its immediate dependencies. </para></listitem>
</varlistentry>
<varlistentry>
<term><option>--root=<replaceable>PATH</replaceable></option></term>

View File

@ -345,6 +345,7 @@ possible_common_cc_flags = [
'-Werror=shift-count-overflow',
'-Werror=shift-overflow=2',
'-Werror=undef',
'-Werror=unused-function',
'-Wfloat-equal',
'-Wimplicit-fallthrough=5',
'-Winit-self',

View File

@ -125,7 +125,7 @@ _systemd_analyze() {
elif __contains_word "$verb" ${VERBS[VERIFY]}; then
if [[ $cur = -* ]]; then
comps='--help --version --system --user --global --man=no --generators=yes --root --image'
comps='--help --version --system --user --global --man=no --generators=yes --root --image --recursive-errors=no --recursive-errors=yes --recursive-errors=one'
else
comps=$( compgen -A file -- "$cur" )
compopt -o filenames

View File

@ -89,6 +89,7 @@ _arguments \
'--global[Show global user instance config]' \
'--root=[Add support for root argument]:PATH' \
'--image=[Add support for discrete images]:PATH' \
'--recursive-errors=[When verifying a unit, control dependency verification]:MODE' \
'--no-pager[Do not pipe output into a pager]' \
'--man=[Do (not) check for existence of man pages]:boolean:(1 0)' \
'--order[When generating graph for dot, show only order]' \

View File

@ -11,10 +11,28 @@
#include "manager.h"
#include "pager.h"
#include "path-util.h"
#include "string-table.h"
#include "strv.h"
#include "unit-name.h"
#include "unit-serialize.h"
static void log_syntax_callback(const char *unit, int level, void *userdata) {
Set **s = userdata;
int r;
assert(userdata);
assert(unit);
if (level > LOG_WARNING)
return;
r = set_put_strdup(s, unit);
if (r < 0) {
set_free_free(*s);
*s = POINTER_MAX;
}
}
static int prepare_filename(const char *filename, char **ret) {
int r;
const char *name;
@ -218,13 +236,22 @@ static int verify_unit(Unit *u, bool check_man, const char *root) {
return r;
}
int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, const char *root) {
static void set_destroy_ignore_pointer_max(Set** s) {
if (*s == POINTER_MAX)
return;
set_free_free(*s);
}
int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, RecursiveErrors recursive_errors, const char *root) {
const ManagerTestRunFlags flags =
MANAGER_TEST_RUN_MINIMAL |
MANAGER_TEST_RUN_ENV_GENERATORS |
(recursive_errors == RECURSIVE_ERRORS_NO) * MANAGER_TEST_RUN_IGNORE_DEPENDENCIES |
run_generators * MANAGER_TEST_RUN_GENERATORS;
_cleanup_(manager_freep) Manager *m = NULL;
_cleanup_(set_destroy_ignore_pointer_max) Set *s = NULL;
_unused_ _cleanup_(clear_log_syntax_callback) dummy_t dummy;
Unit *units[strv_length(filenames)];
_cleanup_free_ char *var = NULL;
int r, k, i, count = 0;
@ -233,6 +260,11 @@ int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run
if (strv_isempty(filenames))
return 0;
/* Allow systemd-analyze to hook in a callback function so that it can get
* all the required log data from the function itself without having to rely
* on a global set variable for the same */
set_log_syntax_callback(log_syntax_callback, &s);
/* set the path */
r = generate_path(&var, filenames);
if (r < 0)
@ -283,5 +315,34 @@ int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run
r = k;
}
if (s == POINTER_MAX)
return log_oom();
if (set_isempty(s) || r != 0)
return r;
/* If all previous verifications succeeded, then either the recursive parsing of all the
* associated dependencies with RECURSIVE_ERRORS_YES or the parsing of the specified unit file
* with RECURSIVE_ERRORS_NO must have yielded a syntax warning and hence, a non-empty set. */
if (IN_SET(recursive_errors, RECURSIVE_ERRORS_YES, RECURSIVE_ERRORS_NO))
return -ENOTRECOVERABLE;
/* If all previous verifications succeeded, then the non-empty set could have resulted from
* a syntax warning encountered during the recursive parsing of the specified unit file and
* its direct dependencies. Hence, search for any of the filenames in the set and if found,
* return a non-zero process exit status. */
if (recursive_errors == RECURSIVE_ERRORS_ONE)
STRV_FOREACH(filename, filenames)
if (set_contains(s, basename(*filename)))
return -ENOTRECOVERABLE;
return 0;
}
static const char* const recursive_errors_table[_RECURSIVE_ERRORS_MAX] = {
[RECURSIVE_ERRORS_NO] = "no",
[RECURSIVE_ERRORS_YES] = "yes",
[RECURSIVE_ERRORS_ONE] = "one",
};
DEFINE_STRING_TABLE_LOOKUP(recursive_errors, RecursiveErrors);

View File

@ -6,5 +6,16 @@
#include "execute.h"
#include "path-lookup.h"
typedef enum RecursiveErrors {
RECURSIVE_ERRORS_YES, /* Look for errors in all associated units */
RECURSIVE_ERRORS_NO, /* Don't look for errors in any but the selected unit */
RECURSIVE_ERRORS_ONE, /* Look for errors in the selected unit and its direct dependencies */
_RECURSIVE_ERRORS_MAX,
_RECURSIVE_ERRORS_INVALID = -EINVAL,
} RecursiveErrors;
int verify_executable(Unit *u, const ExecCommand *exec, const char *root);
int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, const char *root);
int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, RecursiveErrors recursive_errors, const char *root);
const char* recursive_errors_to_string(RecursiveErrors i) _const_;
RecursiveErrors recursive_errors_from_string(const char *s) _pure_;

View File

@ -46,6 +46,7 @@
#endif
#include "sort-util.h"
#include "special.h"
#include "string-table.h"
#include "strv.h"
#include "strxcpyx.h"
#include "terminal-util.h"
@ -85,6 +86,7 @@ static PagerFlags arg_pager_flags = 0;
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
static const char *arg_host = NULL;
static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
static RecursiveErrors arg_recursive_errors = RECURSIVE_ERRORS_YES;
static bool arg_man = true;
static bool arg_generators = false;
static char *arg_root = NULL;
@ -2145,7 +2147,7 @@ static int do_condition(int argc, char *argv[], void *userdata) {
}
static int do_verify(int argc, char *argv[], void *userdata) {
return verify_units(strv_skip(argv, 1), arg_scope, arg_man, arg_generators, arg_root);
return verify_units(strv_skip(argv, 1), arg_scope, arg_man, arg_generators, arg_recursive_errors, arg_root);
}
static int do_security(int argc, char *argv[], void *userdata) {
@ -2180,25 +2182,32 @@ static int help(int argc, char *argv[], void *userdata) {
"%sProfile systemd, show unit dependencies, check unit files.%s\n"
"\nCommands:\n"
" [time] Print time required to boot the machine\n"
" blame Print list of running units ordered by time to init\n"
" critical-chain [UNIT...] Print a tree of the time critical chain of units\n"
" plot Output SVG graphic showing service initialization\n"
" blame Print list of running units ordered by\n"
" time to init\n"
" critical-chain [UNIT...] Print a tree of the time critical chain\n"
" of units\n"
" plot Output SVG graphic showing service\n"
" initialization\n"
" dot [UNIT...] Output dependency graph in %s format\n"
" dump Output state serialization of service manager\n"
" dump Output state serialization of service\n"
" manager\n"
" cat-config Show configuration file and drop-ins\n"
" unit-files List files and symlinks for units\n"
" unit-paths List load directories for units\n"
" exit-status [STATUS...] List exit status definitions\n"
" capability [CAP...] List capability definitions\n"
" syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
" syscall-filter [NAME...] Print list of syscalls in seccomp\n"
" filter\n"
" condition CONDITION... Evaluate conditions and asserts\n"
" verify FILE... Check unit files for correctness\n"
" calendar SPEC... Validate repetitive calendar time events\n"
" calendar SPEC... Validate repetitive calendar time\n"
" events\n"
" timestamp TIMESTAMP... Validate a timestamp\n"
" timespan SPAN... Validate a time span\n"
" security [UNIT...] Analyze security of unit\n"
"\nOptions:\n"
" -h --help Show this help\n"
" --recursive-errors=MODE Control which units are verified\n"
" --version Show package version\n"
" --no-pager Do not pipe output into a pager\n"
" --system Operate on system systemd instance\n"
@ -2210,12 +2219,14 @@ static int help(int argc, char *argv[], void *userdata) {
" --require Show only requirement in the graph\n"
" --from-pattern=GLOB Show only origins in the graph\n"
" --to-pattern=GLOB Show only destinations in the graph\n"
" --fuzz=SECONDS Also print services which finished SECONDS earlier\n"
" than the latest in the branch\n"
" --fuzz=SECONDS Also print services which finished SECONDS\n"
" earlier than the latest in the branch\n"
" --man[=BOOL] Do [not] check for existence of man pages\n"
" --generators[=BOOL] Do [not] run unit generators (requires privileges)\n"
" --generators[=BOOL] Do [not] run unit generators\n"
" (requires privileges)\n"
" --iterations=N Show the specified number of iterations\n"
" --base-time=TIMESTAMP Calculate calendar times relative to specified time\n"
" --base-time=TIMESTAMP Calculate calendar times relative to\n"
" specified time\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
@ -2247,6 +2258,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_GENERATORS,
ARG_ITERATIONS,
ARG_BASE_TIME,
ARG_RECURSIVE_ERRORS,
};
static const struct option options[] = {
@ -2256,6 +2268,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "require", no_argument, NULL, ARG_REQUIRE },
{ "root", required_argument, NULL, ARG_ROOT },
{ "image", required_argument, NULL, ARG_IMAGE },
{ "recursive-errors", required_argument, NULL, ARG_RECURSIVE_ERRORS },
{ "system", no_argument, NULL, ARG_SYSTEM },
{ "user", no_argument, NULL, ARG_USER },
{ "global", no_argument, NULL, ARG_GLOBAL },
@ -2283,6 +2296,18 @@ static int parse_argv(int argc, char *argv[]) {
case 'h':
return help(0, NULL, NULL);
case ARG_RECURSIVE_ERRORS:
if (streq(optarg, "help")) {
DUMP_STRING_TABLE(recursive_errors, RecursiveErrors, _RECURSIVE_ERRORS_MAX);
return 0;
}
r = recursive_errors_from_string(optarg);
if (r < 0)
return log_error_errno(r, "Unknown mode passed to --recursive-errors='%s'.", optarg);
arg_recursive_errors = r;
break;
case ARG_VERSION:
return version();

View File

@ -39,6 +39,9 @@
#define SNDBUF_SIZE (8*1024*1024)
static log_syntax_callback_t log_syntax_callback = NULL;
static void *log_syntax_callback_userdata = NULL;
static LogTarget log_target = LOG_TARGET_CONSOLE;
static int log_max_level = LOG_INFO;
static int log_facility = LOG_DAEMON;
@ -1341,6 +1344,14 @@ void log_received_signal(int level, const struct signalfd_siginfo *si) {
signal_to_string(si->ssi_signo));
}
void set_log_syntax_callback(log_syntax_callback_t cb, void *userdata) {
assert(!log_syntax_callback || !cb);
assert(!log_syntax_callback_userdata || !userdata);
log_syntax_callback = cb;
log_syntax_callback_userdata = userdata;
}
int log_syntax_internal(
const char *unit,
int level,
@ -1352,6 +1363,9 @@ int log_syntax_internal(
const char *func,
const char *format, ...) {
if (log_syntax_callback)
log_syntax_callback(unit, level, log_syntax_callback_userdata);
PROTECT_ERRNO;
char buffer[LINE_MAX];
va_list ap;

View File

@ -32,6 +32,15 @@ typedef enum LogTarget{
#define IS_SYNTHETIC_ERRNO(val) ((val) >> 30 & 1)
#define ERRNO_VALUE(val) (abs(val) & 255)
/* The callback function to be invoked when syntax warnings are seen
* in the unit files. */
typedef void (*log_syntax_callback_t)(const char *unit, int level, void *userdata);
void set_log_syntax_callback(log_syntax_callback_t cb, void *userdata);
static inline void clear_log_syntax_callback(dummy_t *dummy) {
set_log_syntax_callback(/* cb= */ NULL, /* userdata= */ NULL);
}
const char *log_target_to_string(LogTarget target) _const_;
LogTarget log_target_from_string(const char *s) _pure_;
void log_set_target(LogTarget target);

View File

@ -23,28 +23,12 @@
#define _packed_ __attribute__((__packed__))
#define _malloc_ __attribute__((__malloc__))
#define _weak_ __attribute__((__weak__))
#define _likely_(x) (__builtin_expect(!!(x), 1))
#define _unlikely_(x) (__builtin_expect(!!(x), 0))
#define _public_ __attribute__((__visibility__("default")))
#define _hidden_ __attribute__((__visibility__("hidden")))
#define _weakref_(x) __attribute__((__weakref__(#x)))
#define _alignas_(x) __attribute__((__aligned__(__alignof(x))))
#define _alignptr_ __attribute__((__aligned__(sizeof(void*))))
#define _warn_unused_result_ __attribute__((__warn_unused_result__))
#if __GNUC__ >= 7
#define _fallthrough_ __attribute__((__fallthrough__))
#else
#define _fallthrough_
#endif
/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
* compiler versions */
#ifndef _noreturn_
#if __STDC_VERSION__ >= 201112L
#define _noreturn_ _Noreturn
#else
#define _noreturn_ __attribute__((__noreturn__))
#endif
#endif
#if !defined(HAS_FEATURE_MEMORY_SANITIZER)
# if defined(__has_feature)
@ -209,13 +193,6 @@ static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) {
return m;
}
/*
* STRLEN - return the length of a string literal, minus the trailing NUL byte.
* Contrary to strlen(), this is a constant expression.
* @x: a string literal.
*/
#define STRLEN(x) (sizeof(""x"") - 1U)
/*
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
@ -483,4 +460,10 @@ static inline size_t size_add(size_t x, size_t y) {
return y >= SIZE_MAX - x ? SIZE_MAX : x + y;
}
typedef struct {
int _empty[0];
} dummy_t;
assert_cc(sizeof(dummy_t) == 0);
#include "log.h"

15
src/boot/efi/assert.c Normal file
View File

@ -0,0 +1,15 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#ifndef NDEBUG
#include <efi.h>
#include <efilib.h>
#include "util.h"
void efi_assert(const char *expr, const char *file, unsigned line, const char *function) {
log_error_stall(L"systemd-boot assertion '%a' failed at %a:%u, function %a(). Halting.", expr, file, line, function);
for (;;)
uefi_call_wrapper(BS->Stall, 1, 60 * 1000 * 1000);
}
#endif

View File

@ -72,6 +72,9 @@ typedef struct {
} Config;
static VOID cursor_left(UINTN *cursor, UINTN *first) {
assert(cursor);
assert(first);
if ((*cursor) > 0)
(*cursor)--;
else if ((*first) > 0)
@ -84,6 +87,9 @@ static VOID cursor_right(
UINTN x_max,
UINTN len) {
assert(cursor);
assert(first);
if ((*cursor)+1 < x_max)
(*cursor)++;
else if ((*first) + (*cursor) < len)
@ -100,6 +106,8 @@ static BOOLEAN line_edit(
UINTN size, len, first, cursor, clear;
BOOLEAN exit, enter;
assert(line_out);
if (!line_in)
line_in = L"";
size = StrLen(line_in) + 1024;
@ -134,7 +142,7 @@ static BOOLEAN line_edit(
uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, print);
uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
err = console_key_read(&key, TRUE);
err = console_key_read(&key, 0);
if (EFI_ERROR(err))
continue;
@ -332,6 +340,8 @@ static BOOLEAN line_edit(
}
static UINTN entry_lookup_key(Config *config, UINTN start, CHAR16 key) {
assert(config);
if (key == 0)
return -1;
@ -362,6 +372,9 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
_cleanup_freepool_ CHAR16 *partstr = NULL, *defaultstr = NULL;
UINTN x, y;
assert(config);
assert(loaded_image_path);
uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
@ -387,7 +400,7 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
Print(L"OsIndicationsSupported: %d\n", indvar);
Print(L"\n--- press key ---\n\n");
console_key_read(&key, TRUE);
console_key_read(&key, 0);
Print(L"timeout: %u\n", config->timeout_sec);
if (config->timeout_sec_efivar >= 0)
@ -432,7 +445,7 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
Print(L"LoaderEntryDefault: %s\n", defaultstr);
Print(L"\n--- press key ---\n\n");
console_key_read(&key, TRUE);
console_key_read(&key, 0);
for (UINTN i = 0; i < config->entry_count; i++) {
ConfigEntry *entry;
@ -482,7 +495,7 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
entry->path, entry->next_name);
Print(L"\n--- press key ---\n\n");
console_key_read(&key, TRUE);
console_key_read(&key, 0);
}
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
@ -493,6 +506,10 @@ static BOOLEAN menu_run(
ConfigEntry **chosen_entry,
CHAR16 *loaded_image_path) {
assert(config);
assert(chosen_entry);
assert(loaded_image_path);
EFI_STATUS err;
UINTN visible_max;
UINTN idx_highlight;
@ -509,11 +526,10 @@ static BOOLEAN menu_run(
UINTN y_max;
CHAR16 *status;
CHAR16 *clearline;
INTN timeout_remain;
UINTN timeout_remain = config->timeout_sec;
INT16 idx;
BOOLEAN exit = FALSE;
BOOLEAN run = TRUE;
BOOLEAN wait = FALSE;
graphics_mode(FALSE);
uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE);
@ -527,7 +543,7 @@ static BOOLEAN menu_run(
err = console_set_mode(&config->console_mode, config->console_mode_change);
if (EFI_ERROR(err)) {
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
Print(L"Error switching console mode to %ld: %r.\r", (UINT64)config->console_mode, err);
log_error_stall(L"Error switching console mode to %lu: %r", (UINT64)config->console_mode, err);
}
} else
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
@ -538,12 +554,6 @@ static BOOLEAN menu_run(
y_max = 25;
}
/* we check 10 times per second for a keystroke */
if (config->timeout_sec > 0)
timeout_remain = config->timeout_sec * 10;
else
timeout_remain = -1;
idx_highlight = config->idx_default;
idx_highlight_prev = 0;
@ -643,7 +653,7 @@ static BOOLEAN menu_run(
if (timeout_remain > 0) {
FreePool(status);
status = PoolPrint(L"Boot in %d sec.", (timeout_remain + 5) / 10);
status = PoolPrint(L"Boot in %d s.", timeout_remain);
}
/* print status at last line of screen */
@ -664,27 +674,18 @@ static BOOLEAN menu_run(
uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1 + x + len);
}
err = console_key_read(&key, wait);
if (EFI_ERROR(err)) {
/* timeout reached */
err = console_key_read(&key, timeout_remain > 0 ? 1000 * 1000 : 0);
if (err == EFI_TIMEOUT) {
timeout_remain--;
if (timeout_remain == 0) {
exit = TRUE;
break;
}
/* sleep and update status */
if (timeout_remain > 0) {
uefi_call_wrapper(BS->Stall, 1, 100 * 1000);
timeout_remain--;
/* update status */
continue;
}
/* timeout disabled, wait for next key */
wait = TRUE;
continue;
}
timeout_remain = -1;
} else
timeout_remain = 0;
/* clear status after keystroke */
if (status) {
@ -787,7 +788,7 @@ static BOOLEAN menu_run(
config->timeout_sec_efivar,
EFI_VARIABLE_NON_VOLATILE);
if (config->timeout_sec_efivar > 0)
status = PoolPrint(L"Menu timeout set to %d sec.", config->timeout_sec_efivar);
status = PoolPrint(L"Menu timeout set to %d s.", config->timeout_sec_efivar);
else
status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
} else if (config->timeout_sec_efivar <= 0){
@ -795,7 +796,7 @@ static BOOLEAN menu_run(
efivar_set(
LOADER_GUID, L"LoaderConfigTimeout", NULL, EFI_VARIABLE_NON_VOLATILE);
if (config->timeout_sec_config > 0)
status = PoolPrint(L"Menu timeout of %d sec is defined by configuration file.",
status = PoolPrint(L"Menu timeout of %d s is defined by configuration file.",
config->timeout_sec_config);
else
status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
@ -813,7 +814,7 @@ static BOOLEAN menu_run(
config->timeout_sec_efivar,
EFI_VARIABLE_NON_VOLATILE);
if (config->timeout_sec_efivar > 0)
status = PoolPrint(L"Menu timeout set to %d sec.",
status = PoolPrint(L"Menu timeout set to %d s.",
config->timeout_sec_efivar);
else
status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
@ -884,6 +885,9 @@ static BOOLEAN menu_run(
}
static VOID config_add_entry(Config *config, ConfigEntry *entry) {
assert(config);
assert(entry);
if ((config->entry_count & 15) == 0) {
UINTN i;
@ -924,6 +928,12 @@ static CHAR8 *line_get_key_value(
CHAR8 *line, *value;
UINTN linelen;
assert(content);
assert(sep);
assert(pos);
assert(key_ret);
assert(value_ret);
skip:
line = content + *pos;
if (*line == '\0')
@ -985,6 +995,10 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
CHAR8 *line;
UINTN pos = 0;
CHAR8 *key, *value;
EFI_STATUS err;
assert(config);
assert(content);
while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) {
if (strcmpa((CHAR8 *)"timeout", key) == 0) {
@ -1004,32 +1018,23 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
}
if (strcmpa((CHAR8 *)"editor", key) == 0) {
BOOLEAN on;
if (EFI_ERROR(parse_boolean(value, &on)))
continue;
config->editor = on;
err = parse_boolean(value, &config->editor);
if (EFI_ERROR(err))
log_error_stall(L"Error parsing 'editor' config option: %a", value);
continue;
}
if (strcmpa((CHAR8 *)"auto-entries", key) == 0) {
BOOLEAN on;
if (EFI_ERROR(parse_boolean(value, &on)))
continue;
config->auto_entries = on;
err = parse_boolean(value, &config->auto_entries);
if (EFI_ERROR(err))
log_error_stall(L"Error parsing 'auto-entries' config option: %a", value);
continue;
}
if (strcmpa((CHAR8 *)"auto-firmware", key) == 0) {
BOOLEAN on;
if (EFI_ERROR(parse_boolean(value, &on)))
continue;
config->auto_firmware = on;
err = parse_boolean(value, &config->auto_firmware);
if (EFI_ERROR(err))
log_error_stall(L"Error parsing 'auto-firmware' config option: %a", value);
continue;
}
@ -1061,8 +1066,11 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
else {
BOOLEAN on;
if (EFI_ERROR(parse_boolean(value, &on)))
err = parse_boolean(value, &on);
if (EFI_ERROR(err)) {
log_error_stall(L"Error parsing 'random-seed-mode' config option: %a", value);
continue;
}
config->random_seed_mode = on ? RANDOM_SEED_ALWAYS : RANDOM_SEED_OFF;
}
@ -1079,6 +1087,10 @@ static VOID config_entry_parse_tries(
UINTN left = UINTN_MAX, done = UINTN_MAX, factor = 1, i, next_left, next_done;
_cleanup_freepool_ CHAR16 *prefix = NULL;
assert(entry);
assert(path);
assert(file);
/*
* Parses a suffix of two counters (one going down, one going up) in the form "+LEFT-DONE" from the end of the
* filename (but before the .efi/.conf suffix), where the "-DONE" part is optional and may be left out (in
@ -1196,6 +1208,9 @@ static VOID config_entry_bump_counters(
UINTN file_info_size, a, b;
EFI_STATUS r;
assert(entry);
assert(root_dir);
if (entry->tries_left == UINTN_MAX)
return;
@ -1221,8 +1236,7 @@ static VOID config_entry_bump_counters(
break;
if (r != EFI_BUFFER_TOO_SMALL || file_info_size * 2 < file_info_size) {
Print(L"\nFailed to get file info for '%s': %r\n", old_path, r);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
log_error_stall(L"Failed to get file info for '%s': %r", old_path, r);
return;
}
@ -1234,8 +1248,7 @@ static VOID config_entry_bump_counters(
StrCpy(file_info->FileName, entry->next_name);
r = uefi_call_wrapper(handle->SetInfo, 4, handle, &EfiFileInfoGuid, file_info_size, file_info);
if (EFI_ERROR(r)) {
Print(L"\nFailed to rename '%s' to '%s', ignoring: %r\n", old_path, entry->next_name, r);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
log_error_stall(L"Failed to rename '%s' to '%s', ignoring: %r", old_path, entry->next_name, r);
return;
}
@ -1271,6 +1284,14 @@ static VOID config_entry_add_from_file(
EFI_FILE_HANDLE handle;
_cleanup_freepool_ CHAR16 *initrd = NULL;
assert(config);
assert(device);
assert(root_dir);
assert(path);
assert(file);
assert(content);
assert(loaded_image_path);
entry = AllocatePool(sizeof(ConfigEntry));
*entry = (ConfigEntry) {
@ -1399,6 +1420,8 @@ static VOID config_load_defaults(Config *config, EFI_FILE *root_dir) {
UINTN sec;
EFI_STATUS err;
assert(root_dir);
*config = (Config) {
.editor = TRUE,
.auto_entries = TRUE,
@ -1436,6 +1459,11 @@ static VOID config_load_entries(
EFI_FILE_HANDLE entries_dir;
EFI_STATUS err;
assert(config);
assert(device);
assert(root_dir);
assert(loaded_image_path);
err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &entries_dir, (CHAR16*) L"\\loader\\entries", EFI_FILE_MODE_READ, 0ULL);
if (!EFI_ERROR(err)) {
for (;;) {
@ -1471,6 +1499,9 @@ static VOID config_load_entries(
static INTN config_entry_compare(ConfigEntry *a, ConfigEntry *b) {
INTN r;
assert(a);
assert(b);
/* Order entries that have no tries left to the beginning of the list */
if (a->tries_left != 0 && b->tries_left == 0)
return 1;
@ -1501,6 +1532,8 @@ static INTN config_entry_compare(ConfigEntry *a, ConfigEntry *b) {
}
static VOID config_sort_entries(Config *config) {
assert(config);
for (UINTN i = 1; i < config->entry_count; i++) {
BOOLEAN more;
@ -1522,6 +1555,9 @@ static VOID config_sort_entries(Config *config) {
}
static INTN config_entry_find(Config *config, CHAR16 *id) {
assert(config);
assert(id);
for (UINTN i = 0; i < config->entry_count; i++)
if (StrCmp(config->entries[i]->id, id) == 0)
return (INTN) i;
@ -1534,6 +1570,8 @@ static VOID config_default_entry_select(Config *config) {
EFI_STATUS err;
INTN i;
assert(config);
/*
* The EFI variable to specify a boot entry for the next, and only the
* next reboot. The variable is always cleared directly after it is read.
@ -1604,6 +1642,8 @@ static VOID config_default_entry_select(Config *config) {
static BOOLEAN find_nonunique(ConfigEntry **entries, UINTN entry_count) {
BOOLEAN non_unique = FALSE;
assert(entries);
for (UINTN i = 0; i < entry_count; i++)
entries[i]->non_unique = FALSE;
@ -1622,6 +1662,8 @@ static BOOLEAN find_nonunique(ConfigEntry **entries, UINTN entry_count) {
/* generate a unique title, avoiding non-distinguishable menu entries */
static VOID config_title_generate(Config *config) {
assert(config);
/* set title */
for (UINTN i = 0; i < config->entry_count; i++) {
CHAR16 *title;
@ -1694,6 +1736,11 @@ static BOOLEAN config_entry_add_call(
ConfigEntry *entry;
assert(config);
assert(id);
assert(title);
assert(call);
entry = AllocatePool(sizeof(ConfigEntry));
*entry = (ConfigEntry) {
.id = StrDuplicate(id),
@ -1720,11 +1767,17 @@ static ConfigEntry *config_entry_add_loader(
ConfigEntry *entry;
assert(config);
assert(device);
assert(id);
assert(title);
assert(loader);
entry = AllocatePool(sizeof(ConfigEntry));
*entry = (ConfigEntry) {
.type = type,
.title = StrDuplicate(title),
.version = StrDuplicate(version),
.version = version ? StrDuplicate(version) : NULL,
.device = device,
.loader = StrDuplicate(loader),
.id = StrDuplicate(id),
@ -1753,6 +1806,13 @@ static BOOLEAN config_entry_add_loader_auto(
ConfigEntry *entry;
EFI_STATUS err;
assert(config);
assert(device);
assert(root_dir);
assert(id);
assert(title);
assert(loader);
if (!config->auto_entries)
return FALSE;
@ -1793,6 +1853,8 @@ static VOID config_entry_add_osx(Config *config) {
UINTN handle_count = 0;
_cleanup_freepool_ EFI_HANDLE *handles = NULL;
assert(config);
if (!config->auto_entries)
return;
@ -1823,6 +1885,10 @@ static VOID config_entry_add_linux(
EFI_STATUS err;
ConfigEntry *entry;
assert(config);
assert(device);
assert(root_dir);
err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &linux_dir, (CHAR16*) L"\\EFI\\Linux", EFI_FILE_MODE_READ, 0ULL);
if (EFI_ERROR(err))
return;
@ -1957,6 +2023,9 @@ static EFI_DEVICE_PATH *path_parent(EFI_DEVICE_PATH *path, EFI_DEVICE_PATH *node
EFI_DEVICE_PATH *parent;
UINTN len;
assert(path);
assert(node);
len = (UINT8*) NextDevicePathNode(node) - (UINT8*) path;
parent = (EFI_DEVICE_PATH*) AllocatePool(len + sizeof(EFI_DEVICE_PATH));
CopyMem(parent, path, len);
@ -1978,6 +2047,9 @@ static VOID config_load_xbootldr(
EFI_FILE *root_dir;
EFI_STATUS r;
assert(config);
assert(device);
partition_path = DevicePathFromHandle(device);
if (!partition_path)
return;
@ -2164,19 +2236,16 @@ static EFI_STATUS image_start(
CHAR16 *options;
EFI_STATUS err;
assert(config);
assert(entry);
path = FileDevicePath(entry->device, entry->loader);
if (!path) {
Print(L"Error getting device path.");
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return EFI_INVALID_PARAMETER;
}
if (!path)
return log_error_status_stall(EFI_INVALID_PARAMETER, L"Error getting device path.");
err = uefi_call_wrapper(BS->LoadImage, 6, FALSE, parent_image, path, NULL, 0, &image);
if (EFI_ERROR(err)) {
Print(L"Error loading %s: %r", entry->loader, err);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return err;
}
if (EFI_ERROR(err))
return log_error_status_stall(err, L"Error loading %s: %r", entry->loader, err);
if (config->options_edit)
options = config->options_edit;
@ -2190,22 +2259,19 @@ static EFI_STATUS image_start(
err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (EFI_ERROR(err)) {
Print(L"Error getting LoadedImageProtocol handle: %r", err);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
log_error_stall(L"Error getting LoadedImageProtocol handle: %r", err);
goto out_unload;
}
loaded_image->LoadOptions = options;
loaded_image->LoadOptionsSize = (StrLen(loaded_image->LoadOptions)+1) * sizeof(CHAR16);
loaded_image->LoadOptionsSize = StrSize(loaded_image->LoadOptions);
#if ENABLE_TPM
/* Try to log any options to the TPM, especially to catch manually edited options */
err = tpm_log_event(SD_TPM_PCR,
(EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->LoadOptions,
loaded_image->LoadOptionsSize, loaded_image->LoadOptions);
if (EFI_ERROR(err)) {
Print(L"Unable to add image options measurement: %r", err);
uefi_call_wrapper(BS->Stall, 1, 200 * 1000);
}
if (EFI_ERROR(err))
log_error_stall(L"Unable to add image options measurement: %r", err);
#endif
}
@ -2231,12 +2297,11 @@ static EFI_STATUS reboot_into_firmware(VOID) {
return err;
err = uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, EFI_SUCCESS, 0, NULL);
Print(L"Error calling ResetSystem: %r", err);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return err;
return log_error_status_stall(err, L"Error calling ResetSystem: %r", err);
}
static VOID config_free(Config *config) {
assert(config);
for (UINTN i = 0; i < config->entry_count; i++)
config_entry_free(config->entries[i]);
FreePool(config->entries);
@ -2246,26 +2311,30 @@ static VOID config_free(Config *config) {
}
static VOID config_write_entries_to_variable(Config *config) {
_cleanup_freepool_ CHAR16 *buffer = NULL;
_cleanup_freepool_ CHAR8 *buffer = NULL;
UINTN sz = 0;
CHAR16 *p;
CHAR8 *p;
assert(config);
for (UINTN i = 0; i < config->entry_count; i++)
sz += StrLen(config->entries[i]->id) + 1;
sz += StrSize(config->entries[i]->id);
p = buffer = AllocatePool(sz * sizeof(CHAR16));
p = buffer = AllocatePool(sz);
for (UINTN i = 0; i < config->entry_count; i++) {
UINTN l;
l = StrLen(config->entries[i]->id) + 1;
CopyMem(p, config->entries[i]->id, l * sizeof(CHAR16));
l = StrSize(config->entries[i]->id);
CopyMem(p, config->entries[i]->id, l);
p += l;
}
assert(p == buffer + sz);
/* Store the full list of discovered entries. */
(void) efivar_set_raw(LOADER_GUID, L"LoaderEntries", buffer, (UINT8 *) p - (UINT8 *) buffer, 0);
(void) efivar_set_raw(LOADER_GUID, L"LoaderEntries", buffer, sz, 0);
}
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
@ -2305,30 +2374,21 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (EFI_ERROR(err)) {
Print(L"Error getting a LoadedImageProtocol handle: %r", err);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return err;
}
if (EFI_ERROR(err))
return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err);
/* export the device path this image is started from */
if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS)
efivar_set(LOADER_GUID, L"LoaderDevicePartUUID", uuid, 0);
root_dir = LibOpenRoot(loaded_image->DeviceHandle);
if (!root_dir) {
Print(L"Unable to open root directory.");
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return EFI_LOAD_ERROR;
}
if (!root_dir)
return log_error_status_stall(EFI_LOAD_ERROR, L"Unable to open root directory.", EFI_LOAD_ERROR);
if (secure_boot_enabled() && shim_loaded()) {
err = security_policy_install();
if (EFI_ERROR(err)) {
Print(L"Error installing security policy: %r ", err);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return err;
}
if (EFI_ERROR(err))
return log_error_status_stall(err, L"Error installing security policy: %r", err);
}
/* the filesystem path to this image, to prevent adding ourselves to the menu */
@ -2367,8 +2427,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
}
if (config.entry_count == 0) {
Print(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
log_error_stall(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
goto out;
}
@ -2392,13 +2451,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
else {
UINT64 key;
err = console_key_read(&key, FALSE);
if (err == EFI_NOT_READY) {
uefi_call_wrapper(BS->Stall, 1, 100 * 1000);
err = console_key_read(&key, FALSE);
}
/* Block up to 100ms to give firmware time to get input working. */
err = console_key_read(&key, 100 * 1000);
if (!EFI_ERROR(err)) {
INT16 idx;
@ -2440,8 +2494,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
err = image_start(image, &config, entry);
if (EFI_ERROR(err)) {
graphics_mode(FALSE);
Print(L"\nFailed to execute %s (%s): %r\n", entry->title, entry->loader, err);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
log_error_stall(L"Failed to execute %s (%s): %r", entry->title, entry->loader, err);
goto out;
}

View File

@ -11,32 +11,82 @@
#define EFI_SIMPLE_TEXT_INPUT_EX_GUID &(EFI_GUID) EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID
EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait) {
static inline void EventClosep(EFI_EVENT *event) {
if (!*event)
return;
uefi_call_wrapper(BS->CloseEvent, 1, *event);
}
/*
* Reading input from the console sounds like an easy task to do, but thanks to broken
* firmware it is actually a nightmare.
*
* There is a ConIn and TextInputEx API for this. Ideally we want to use TextInputEx,
* because that gives us Ctrl/Alt/Shift key state information. Unfortunately, it is not
* always available and sometimes just non-functional.
*
* On the other hand we have ConIn, where some firmware likes to just freeze on us
* if we call ReadKeyStroke on it.
*
* Therefore, we use WaitForEvent on both ConIn and TextInputEx (if available) along
* with a timer event. The timer ensures there is no need to call into functions
* that might freeze on us, while still allowing us to show a timeout counter.
*/
EFI_STATUS console_key_read(UINT64 *key, UINT64 timeout_usec) {
static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInputEx;
static BOOLEAN checked;
UINTN index;
EFI_INPUT_KEY k;
EFI_STATUS err;
_cleanup_(EventClosep) EFI_EVENT timer = NULL;
EFI_EVENT events[3] = { ST->ConIn->WaitForKey };
UINTN n_events = 1;
assert(key);
if (!checked) {
err = LibLocateProtocol(EFI_SIMPLE_TEXT_INPUT_EX_GUID, (VOID **)&TextInputEx);
if (EFI_ERROR(err))
if (EFI_ERROR(err) ||
uefi_call_wrapper(BS->CheckEvent, 1, TextInputEx->WaitForKeyEx) == EFI_INVALID_PARAMETER)
/* If WaitForKeyEx fails here, the firmware pretends it talks this
* protocol, but it really doesn't. */
TextInputEx = NULL;
else
events[n_events++] = TextInputEx->WaitForKeyEx;
checked = TRUE;
}
/* wait until key is pressed */
if (wait)
uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &index);
if (timeout_usec > 0) {
err = uefi_call_wrapper(BS->CreateEvent, 5, EVT_TIMER, 0, NULL, NULL, &timer);
if (EFI_ERROR(err))
return log_error_status_stall(err, L"Error creating timer event: %r", err);
if (TextInputEx) {
/* SetTimer expects 100ns units for some reason. */
err = uefi_call_wrapper(BS->SetTimer, 3, timer, TimerRelative, timeout_usec * 10);
if (EFI_ERROR(err))
return log_error_status_stall(err, L"Error arming timer event: %r", err);
events[n_events++] = timer;
}
err = uefi_call_wrapper(BS->WaitForEvent, 3, n_events, events, &index);
if (EFI_ERROR(err))
return log_error_status_stall(err, L"Error waiting for events: %r", err);
if (timeout_usec > 0 && timer == events[index])
return EFI_TIMEOUT;
/* TextInputEx might be ready too even if ConIn got to signal first. */
if (TextInputEx && !EFI_ERROR(uefi_call_wrapper(BS->CheckEvent, 1, TextInputEx->WaitForKeyEx))) {
EFI_KEY_DATA keydata;
UINT64 keypress;
UINT32 shift = 0;
err = uefi_call_wrapper(TextInputEx->ReadKeyStrokeEx, 2, TextInputEx, &keydata);
if (!EFI_ERROR(err)) {
UINT32 shift = 0;
if (EFI_ERROR(err))
return err;
/* do not distinguish between left and right keys */
if (keydata.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) {
@ -50,22 +100,18 @@ EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait) {
keypress = KEYPRESS(shift, keydata.Key.ScanCode, keydata.Key.UnicodeChar);
if (keypress > 0) {
*key = keypress;
return 0;
}
}
return EFI_SUCCESS;
}
return EFI_NOT_READY;
}
/* fallback for firmware which does not support SimpleTextInputExProtocol
*
* This is also called in case ReadKeyStrokeEx did not return a key, because
* some broken firmwares offer SimpleTextInputExProtocol, but never actually
* handle any key. */
err = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &k);
if (EFI_ERROR(err))
return err;
*key = KEYPRESS(0, k.ScanCode, k.UnicodeChar);
return 0;
return EFI_SUCCESS;
}
static EFI_STATUS change_mode(UINTN mode) {
@ -108,6 +154,8 @@ static EFI_STATUS mode_auto(UINTN *mode) {
EFI_STATUS err;
BOOLEAN keep = FALSE;
assert(mode);
err = LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput);
if (!EFI_ERROR(err) && GraphicsOutput->Mode && GraphicsOutput->Mode->Info) {
Info = GraphicsOutput->Mode->Info;
@ -156,6 +204,8 @@ static EFI_STATUS mode_auto(UINTN *mode) {
}
EFI_STATUS console_set_mode(UINTN *mode, enum console_mode_change_type how) {
assert(mode);
if (how == CONSOLE_MODE_AUTO)
return mode_auto(mode);

View File

@ -16,5 +16,5 @@ enum console_mode_change_type {
CONSOLE_MODE_MAX,
};
EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait);
EFI_STATUS console_key_read(UINT64 *key, UINT64 timeout_usec);
EFI_STATUS console_set_mode(UINTN *mode, enum console_mode_change_type how);

View File

@ -42,6 +42,7 @@
*/
#include "crc32.h"
#include "macro-fundamental.h"
static const UINT32 crc32_tab[] = {
0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
@ -111,6 +112,8 @@ UINT32 crc32(UINT32 seed, const VOID *buf, UINTN len) {
const UINT8 *p = buf;
UINT32 crc = seed;
assert(buf);
while (len > 0) {
crc = crc32_add_char(crc, *p++);
len--;
@ -129,6 +132,8 @@ UINT32 crc32_exclude_offset(
const UINT8 *p = buf;
UINT32 crc = seed;
assert(buf);
for (UINTN i = 0; i < len; i++) {
UINT8 x = *p++;

View File

@ -9,6 +9,8 @@
EFI_STATUS disk_get_part_uuid(EFI_HANDLE *handle, CHAR16 uuid[static 37]) {
EFI_DEVICE_PATH *device_path;
assert(handle);
/* export the device path this image is started from */
device_path = DevicePathFromHandle(handle);
if (device_path) {

View File

@ -17,6 +17,8 @@ static VOID linux_efi_handover(EFI_HANDLE image, struct boot_params *params) {
handover_f handover;
UINTN start = (UINTN)params->hdr.code32_start;
assert(params);
#ifdef __x86_64__
asm volatile ("cli");
start += 512;
@ -35,6 +37,9 @@ EFI_STATUS linux_exec(EFI_HANDLE *image,
EFI_PHYSICAL_ADDRESS addr;
EFI_STATUS err;
assert(image);
assert(cmdline);
image_params = (struct boot_params *) linux_addr;
if (image_params->hdr.boot_flag != 0xAA55 ||

View File

@ -5,6 +5,7 @@
#include <efi.h>
#include <efilib.h>
#include "macro-fundamental.h"
#include "measure.h"
#define EFI_TCG_GUID \
@ -184,8 +185,10 @@ static EFI_STATUS tpm1_measure_to_pcr_and_event_log(const EFI_TCG *tcg, UINT32 p
EFI_PHYSICAL_ADDRESS event_log_last;
UINTN desc_len;
desc_len = (StrLen(description) + 1) * sizeof(CHAR16);
assert(tcg);
assert(description);
desc_len = StrSize(description);
tcg_event = AllocateZeroPool(desc_len + sizeof(TCG_PCR_EVENT));
if (!tcg_event)
@ -215,14 +218,16 @@ static EFI_STATUS tpm2_measure_to_pcr_and_event_log(const EFI_TCG2 *tcg, UINT32
EFI_TCG2_EVENT *tcg_event;
UINTN desc_len;
desc_len = StrLen(description) * sizeof(CHAR16);
assert(tcg);
assert(description);
tcg_event = AllocateZeroPool(sizeof(*tcg_event) - sizeof(tcg_event->Event) + desc_len + 1);
desc_len = StrSize(description);
tcg_event = AllocateZeroPool(sizeof(*tcg_event) - sizeof(tcg_event->Event) + desc_len);
if (!tcg_event)
return EFI_OUT_OF_RESOURCES;
tcg_event->Size = sizeof(*tcg_event) - sizeof(tcg_event->Event) + desc_len + 1;
tcg_event->Size = sizeof(*tcg_event) - sizeof(tcg_event->Event) + desc_len;
tcg_event->Header.HeaderSize = sizeof(EFI_TCG2_EVENT_HEADER);
tcg_event->Header.HeaderVersion = EFI_TCG2_EVENT_HEADER_VERSION;
tcg_event->Header.PCRIndex = pcrindex;
@ -302,6 +307,8 @@ EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UIN
EFI_TCG *tpm1;
EFI_TCG2 *tpm2;
assert(description);
tpm2 = tcg2_interface_check();
if (tpm2)
return tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description);

View File

@ -17,6 +17,7 @@ efi_headers = files('''
'''.split())
common_sources = '''
assert.c
disk.c
graphics.c
measure.c
@ -219,12 +220,16 @@ if have_gnu_efi
compile_args += ['-Werror']
endif
if get_option('buildtype') == 'debug'
compile_args += ['-ggdb', '-O0']
compile_args += ['-ggdb', '-O0', '-DEFI_DEBUG']
elif get_option('buildtype') == 'debugoptimized'
compile_args += ['-ggdb', '-Og']
compile_args += ['-ggdb', '-Og', '-DEFI_DEBUG']
else
compile_args += ['-O2']
endif
if get_option('b_ndebug') == 'true' or (
get_option('b_ndebug') == 'if-release' and ['plain', 'release'].contains(get_option('buildtype')))
compile_args += ['-DNDEBUG']
endif
efi_ldflags = ['-T', efi_lds,
'-shared',

View File

@ -66,6 +66,9 @@ EFI_STATUS pe_memory_locate_sections(CHAR8 *base, CHAR8 **sections, UINTN *addrs
struct PeHeader *pe;
UINTN offset;
assert(base);
assert(sections);
dos = (struct DosFileHeader *)base;
if (CompareMem(dos->Magic, "MZ", 2) != 0)
@ -118,6 +121,9 @@ EFI_STATUS pe_file_locate_sections(EFI_FILE *dir, CHAR16 *path, CHAR8 **sections
EFI_STATUS err;
_cleanup_freepool_ CHAR8 *header = NULL;
assert(dir);
assert(path);
err = uefi_call_wrapper(dir->Open, 5, dir, &handle, path, EFI_FILE_MODE_READ, 0ULL);
if (EFI_ERROR(err))
return err;

View File

@ -22,6 +22,8 @@ static EFI_STATUS acquire_rng(UINTN size, VOID **ret) {
EFI_RNG_PROTOCOL *rng;
EFI_STATUS err;
assert(ret);
/* Try to acquire the specified number of bytes from the UEFI RNG */
err = LibLocateProtocol(EFI_RNG_GUID, (VOID**) &rng);
@ -35,10 +37,8 @@ static EFI_STATUS acquire_rng(UINTN size, VOID **ret) {
return log_oom();
err = uefi_call_wrapper(rng->GetRNG, 3, rng, NULL, size, data);
if (EFI_ERROR(err)) {
Print(L"Failed to acquire RNG data: %r\n", err);
return err;
}
if (EFI_ERROR(err))
return log_error_status_stall(err, L"Failed to acquire RNG data: %r", err);
*ret = TAKE_PTR(data);
return EFI_SUCCESS;
@ -65,6 +65,10 @@ static VOID hash_once(
struct sha256_ctx hash;
assert(old_seed);
assert(rng);
assert(system_token);
sha256_init_ctx(&hash);
sha256_process_bytes(old_seed, size, &hash);
if (rng)
@ -87,6 +91,11 @@ static EFI_STATUS hash_many(
_cleanup_freepool_ VOID *output = NULL;
assert(old_seed);
assert(rng);
assert(system_token);
assert(ret);
/* Hashes the specified parameters in counter mode, generating n hash values, with the counter in the
* range counter_startcounter_start+n-1. */
@ -117,6 +126,12 @@ static EFI_STATUS mangle_random_seed(
EFI_STATUS err;
UINTN n;
assert(old_seed);
assert(rng);
assert(system_token);
assert(ret_new_seed);
assert(ret_for_kernel);
/* This takes the old seed file contents, an (optional) random number acquired from the UEFI RNG, an
* (optional) system 'token' installed once by the OS installer in an EFI variable, and hashes them
* together in counter mode, generating a new seed (to replace the file on disk) and the seed for the
@ -146,17 +161,18 @@ static EFI_STATUS acquire_system_token(VOID **ret, UINTN *ret_size) {
EFI_STATUS err;
UINTN size;
assert(ret);
assert(ret_size);
err = efivar_get_raw(LOADER_GUID, L"LoaderSystemToken", &data, &size);
if (EFI_ERROR(err)) {
if (err != EFI_NOT_FOUND)
Print(L"Failed to read LoaderSystemToken EFI variable: %r", err);
log_error_stall(L"Failed to read LoaderSystemToken EFI variable: %r", err);
return err;
}
if (size <= 0) {
Print(L"System token too short, ignoring.");
return EFI_NOT_FOUND;
}
if (size <= 0)
return log_error_status_stall(EFI_NOT_FOUND, L"System token too short, ignoring.");
*ret = TAKE_PTR(data);
*ret_size = size;
@ -166,7 +182,7 @@ static EFI_STATUS acquire_system_token(VOID **ret, UINTN *ret_size) {
static VOID validate_sha256(void) {
#ifndef __OPTIMIZE__
#ifdef EFI_DEBUG
/* Let's validate our SHA256 implementation. We stole it from glibc, and converted it to UEFI
* style. We better check whether it does the right stuff. We use the simpler test vectors from the
* SHA spec. Note that we strip this out in optimization builds. */
@ -208,14 +224,9 @@ static VOID validate_sha256(void) {
sha256_process_bytes(array[i].string, strlena((const CHAR8*) array[i].string), &hash);
sha256_finish_ctx(&hash, result);
if (CompareMem(result, array[i].hash, HASH_VALUE_SIZE) != 0) {
Print(L"SHA256 failed validation.\n");
uefi_call_wrapper(BS->Stall, 1, 120 * 1000 * 1000);
return;
}
assert(CompareMem(result, array[i].hash, HASH_VALUE_SIZE) == 0);
}
Print(L"SHA256 validated\n");
#endif
}
@ -226,6 +237,8 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
_cleanup_freepool_ EFI_FILE_INFO *info = NULL;
EFI_STATUS err;
assert(root_dir);
validate_sha256();
if (mode == RANDOM_SEED_OFF)
@ -246,7 +259,7 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, (CHAR16*) L"\\loader\\random-seed", EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE, 0ULL);
if (EFI_ERROR(err)) {
if (err != EFI_NOT_FOUND && err != EFI_WRITE_PROTECTED)
Print(L"Failed to open random seed file: %r\n", err);
log_error_stall(L"Failed to open random seed file: %r", err);
return err;
}
@ -255,15 +268,11 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
return log_oom();
size = info->FileSize;
if (size < RANDOM_MAX_SIZE_MIN) {
Print(L"Random seed file is too short?\n");
return EFI_INVALID_PARAMETER;
}
if (size < RANDOM_MAX_SIZE_MIN)
return log_error_status_stall(EFI_INVALID_PARAMETER, L"Random seed file is too short.");
if (size > RANDOM_MAX_SIZE_MAX) {
Print(L"Random seed file is too large?\n");
return EFI_INVALID_PARAMETER;
}
if (size > RANDOM_MAX_SIZE_MAX)
return log_error_status_stall(EFI_INVALID_PARAMETER, L"Random seed file is too large.");
seed = AllocatePool(size);
if (!seed)
@ -271,20 +280,14 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
rsize = size;
err = uefi_call_wrapper(handle->Read, 3, handle, &rsize, seed);
if (EFI_ERROR(err)) {
Print(L"Failed to read random seed file: %r\n", err);
return err;
}
if (rsize != size) {
Print(L"Short read on random seed file\n");
return EFI_PROTOCOL_ERROR;
}
if (EFI_ERROR(err))
return log_error_status_stall(err, L"Failed to read random seed file: %r", err);
if (rsize != size)
return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short read on random seed file.");
err = uefi_call_wrapper(handle->SetPosition, 2, handle, 0);
if (EFI_ERROR(err)) {
Print(L"Failed to seek to beginning of random seed file: %r\n", err);
return err;
}
if (EFI_ERROR(err))
return log_error_status_stall(err, L"Failed to seek to beginning of random seed file: %r", err);
/* Request some random data from the UEFI RNG. We don't need this to work safely, but it's a good
* idea to use it because it helps us for cases where users mistakenly include a random seed in
@ -299,27 +302,19 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
/* Update the random seed on disk before we use it */
wsize = size;
err = uefi_call_wrapper(handle->Write, 3, handle, &wsize, new_seed);
if (EFI_ERROR(err)) {
Print(L"Failed to write random seed file: %r\n", err);
return err;
}
if (wsize != size) {
Print(L"Short write on random seed file\n");
return EFI_PROTOCOL_ERROR;
}
if (EFI_ERROR(err))
return log_error_status_stall(err, L"Failed to write random seed file: %r", err);
if (wsize != size)
return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short write on random seed file.");
err = uefi_call_wrapper(handle->Flush, 1, handle);
if (EFI_ERROR(err)) {
Print(L"Failed to flush random seed file: %r\n");
return err;
}
if (EFI_ERROR(err))
return log_error_status_stall(err, L"Failed to flush random seed file: %r", err);
/* We are good to go */
err = efivar_set_raw(LOADER_GUID, L"LoaderRandomSeed", for_kernel, size, 0);
if (EFI_ERROR(err)) {
Print(L"Failed to write random seed to EFI variable: %r\n", err);
return err;
}
if (EFI_ERROR(err))
return log_error_status_stall(err, L"Failed to write random seed to EFI variable: %r", err);
return EFI_SUCCESS;
}

View File

@ -23,6 +23,7 @@
/* Written by Ulrich Drepper <drepper@redhat.com>, 2007. */
#include "macro-fundamental.h"
#include "sha256.h"
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
@ -73,6 +74,8 @@ static void sha256_process_block(const void *, UINTN, struct sha256_ctx *);
/* Initialize structure containing state of computation.
(FIPS 180-2:5.3.2) */
void sha256_init_ctx(struct sha256_ctx *ctx) {
assert(ctx);
ctx->H[0] = 0x6a09e667;
ctx->H[1] = 0xbb67ae85;
ctx->H[2] = 0x3c6ef372;
@ -96,6 +99,9 @@ void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf) {
UINT32 bytes = ctx->buflen;
UINTN pad;
assert(ctx);
assert(resbuf);
/* Now count remaining bytes. */
ctx->total64 += bytes;
@ -118,6 +124,9 @@ void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf) {
}
void sha256_process_bytes(const void *buffer, UINTN len, struct sha256_ctx *ctx) {
assert(buffer);
assert(ctx);
/* When we already have some bits in our internal buffer concatenate
both inputs first. */
@ -191,6 +200,10 @@ void sha256_process_bytes(const void *buffer, UINTN len, struct sha256_ctx *ctx)
static void sha256_process_block(const void *buffer, UINTN len, struct sha256_ctx *ctx) {
const UINT32 *words = buffer;
UINTN nwords = len / sizeof (UINT32);
assert(buffer);
assert(ctx);
UINT32 a = ctx->H[0];
UINT32 b = ctx->H[1];
UINT32 c = ctx->H[2];

View File

@ -111,6 +111,10 @@ static EFIAPI EFI_STATUS security2_policy_authentication (const EFI_SECURITY2_PR
VOID *file_buffer, UINTN file_size, BOOLEAN boot_policy) {
EFI_STATUS status;
assert(this);
assert(device_path);
assert(file_buffer);
/* Chain original security policy */
status = uefi_call_wrapper(es2fa, 5, this, device_path, file_buffer, file_size, boot_policy);
@ -143,6 +147,9 @@ static EFIAPI EFI_STATUS security_policy_authentication (const EFI_SECURITY_PROT
_cleanup_freepool_ CHAR8 *file_buffer = NULL;
UINTN file_size;
assert(this);
assert(device_path_const);
if (!device_path_const)
return EFI_INVALID_PARAMETER;

View File

@ -44,6 +44,11 @@ static EFI_STATUS bmp_parse_header(UINT8 *bmp, UINTN size, struct bmp_dib **ret_
struct bmp_map *map;
UINTN row_size;
assert(bmp);
assert(ret_dib);
assert(ret_map);
assert(pixmap);
if (size < sizeof(struct bmp_file) + sizeof(struct bmp_dib))
return EFI_INVALID_PARAMETER;
@ -128,6 +133,8 @@ static EFI_STATUS bmp_parse_header(UINT8 *bmp, UINTN size, struct bmp_dib **ret_
static VOID pixel_blend(UINT32 *dst, const UINT32 source) {
UINT32 alpha, src, src_rb, src_g, dst_rb, dst_g, rb, g;
assert(dst);
alpha = (source & 0xff);
/* convert src from RGBA to XRGB */
@ -152,6 +159,11 @@ static EFI_STATUS bmp_to_blt(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf,
UINT8 *pixmap) {
UINT8 *in;
assert(buf);
assert(dib);
assert(map);
assert(pixmap);
/* transform and copy pixels */
in = pixmap;
for (UINTN y = 0; y < dib->y; y++) {
@ -247,6 +259,8 @@ EFI_STATUS graphics_splash(UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_
UINTN y_pos = 0;
EFI_STATUS err;
assert(content);
if (!background) {
if (StriCmp(L"Apple", ST->FirmwareVendor) == 0) {
pixel.Red = 0xc0;

View File

@ -36,18 +36,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (EFI_ERROR(err)) {
Print(L"Error getting a LoadedImageProtocol handle: %r ", err);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return err;
}
if (EFI_ERROR(err))
return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err);
err = pe_memory_locate_sections(loaded_image->ImageBase, sections, addrs, offs, szs);
if (EFI_ERROR(err)) {
Print(L"Unable to locate embedded .linux section: %r ", err);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return err;
}
if (EFI_ERROR(err))
return log_error_status_stall(err, L"Unable to locate embedded .linux section: %r", err);
if (szs[0] > 0)
cmdline = (CHAR8 *)(loaded_image->ImageBase) + addrs[0];
@ -72,10 +66,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
err = tpm_log_event(SD_TPM_PCR,
(EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->LoadOptions,
loaded_image->LoadOptionsSize, loaded_image->LoadOptions);
if (EFI_ERROR(err)) {
Print(L"Unable to add image options measurement: %r", err);
uefi_call_wrapper(BS->Stall, 1, 200 * 1000);
}
if (EFI_ERROR(err))
log_error_stall(L"Unable to add image options measurement: %r", err);
#endif
}
@ -126,7 +118,5 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
(UINTN)loaded_image->ImageBase + addrs[2], szs[2]);
graphics_mode(FALSE);
Print(L"Execution of embedded linux image failed: %r\n", err);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return err;
return log_error_status_stall(err, L"Execution of embedded linux image failed: %r", err);
}

View File

@ -67,13 +67,17 @@ UINT64 time_usec(VOID) {
}
EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b) {
assert(b);
if (!v)
return EFI_INVALID_PARAMETER;
if (strcmpa(v, (CHAR8 *)"1") == 0 ||
strcmpa(v, (CHAR8 *)"yes") == 0 ||
strcmpa(v, (CHAR8 *)"y") == 0 ||
strcmpa(v, (CHAR8 *)"true") == 0) {
strcmpa(v, (CHAR8 *)"true") == 0 ||
strcmpa(v, (CHAR8 *)"t") == 0 ||
strcmpa(v, (CHAR8 *)"on") == 0) {
*b = TRUE;
return EFI_SUCCESS;
}
@ -81,7 +85,9 @@ EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b) {
if (strcmpa(v, (CHAR8 *)"0") == 0 ||
strcmpa(v, (CHAR8 *)"no") == 0 ||
strcmpa(v, (CHAR8 *)"n") == 0 ||
strcmpa(v, (CHAR8 *)"false") == 0) {
strcmpa(v, (CHAR8 *)"false") == 0 ||
strcmpa(v, (CHAR8 *)"f") == 0 ||
strcmpa(v, (CHAR8 *)"off") == 0) {
*b = FALSE;
return EFI_SUCCESS;
}
@ -90,24 +96,37 @@ EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b) {
}
EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, const CHAR16 *name, const VOID *buf, UINTN size, UINT32 flags) {
assert(vendor);
assert(name);
assert(buf || size == 0);
flags |= EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
return uefi_call_wrapper(RT->SetVariable, 5, (CHAR16*) name, (EFI_GUID *)vendor, flags, size, (VOID*) buf);
}
EFI_STATUS efivar_set(const EFI_GUID *vendor, const CHAR16 *name, const CHAR16 *value, UINT32 flags) {
return efivar_set_raw(vendor, name, value, value ? (StrLen(value) + 1) * sizeof(CHAR16) : 0, flags);
assert(vendor);
assert(name);
return efivar_set_raw(vendor, name, value, value ? StrSize(value) : 0, flags);
}
EFI_STATUS efivar_set_uint_string(const EFI_GUID *vendor, const CHAR16 *name, UINTN i, UINT32 flags) {
CHAR16 str[32];
SPrint(str, 32, L"%u", i);
assert(vendor);
assert(name);
SPrint(str, ELEMENTSOF(str), L"%u", i);
return efivar_set(vendor, name, str, flags);
}
EFI_STATUS efivar_set_uint32_le(const EFI_GUID *vendor, const CHAR16 *name, UINT32 value, UINT32 flags) {
UINT8 buf[4];
assert(vendor);
assert(name);
buf[0] = (UINT8)(value >> 0U & 0xFF);
buf[1] = (UINT8)(value >> 8U & 0xFF);
buf[2] = (UINT8)(value >> 16U & 0xFF);
@ -119,6 +138,9 @@ EFI_STATUS efivar_set_uint32_le(const EFI_GUID *vendor, const CHAR16 *name, UINT
EFI_STATUS efivar_set_uint64_le(const EFI_GUID *vendor, const CHAR16 *name, UINT64 value, UINT32 flags) {
UINT8 buf[8];
assert(vendor);
assert(name);
buf[0] = (UINT8)(value >> 0U & 0xFF);
buf[1] = (UINT8)(value >> 8U & 0xFF);
buf[2] = (UINT8)(value >> 16U & 0xFF);
@ -132,35 +154,38 @@ EFI_STATUS efivar_set_uint64_le(const EFI_GUID *vendor, const CHAR16 *name, UINT
}
EFI_STATUS efivar_get(const EFI_GUID *vendor, const CHAR16 *name, CHAR16 **value) {
_cleanup_freepool_ CHAR8 *buf = NULL;
_cleanup_freepool_ CHAR16 *buf = NULL;
EFI_STATUS err;
CHAR16 *val;
UINTN size;
err = efivar_get_raw(vendor, name, &buf, &size);
assert(vendor);
assert(name);
err = efivar_get_raw(vendor, name, (CHAR8**)&buf, &size);
if (EFI_ERROR(err))
return err;
/* Make sure there are no incomplete characters in the buffer */
if ((size % 2) != 0)
if ((size % sizeof(CHAR16)) != 0)
return EFI_INVALID_PARAMETER;
if (!value)
return EFI_SUCCESS;
/* Return buffer directly if it happens to be NUL terminated already */
if (size >= 2 && buf[size-2] == 0 && buf[size-1] == 0) {
*value = (CHAR16*) TAKE_PTR(buf);
if (size >= sizeof(CHAR16) && buf[size/sizeof(CHAR16)] == 0) {
*value = TAKE_PTR(buf);
return EFI_SUCCESS;
}
/* Make sure a terminating NUL is available at the end */
val = AllocatePool(size + 2);
val = AllocatePool(size + sizeof(CHAR16));
if (!val)
return EFI_OUT_OF_RESOURCES;
CopyMem(val, buf, size);
val[size/2] = 0; /* NUL terminate */
val[size / sizeof(CHAR16)] = 0; /* NUL terminate */
*value = val;
return EFI_SUCCESS;
@ -170,8 +195,12 @@ EFI_STATUS efivar_get_uint_string(const EFI_GUID *vendor, const CHAR16 *name, UI
_cleanup_freepool_ CHAR16 *val = NULL;
EFI_STATUS err;
assert(vendor);
assert(name);
assert(i);
err = efivar_get(vendor, name, &val);
if (!EFI_ERROR(err) && i)
if (!EFI_ERROR(err))
*i = Atoi(val);
return err;
@ -182,6 +211,9 @@ EFI_STATUS efivar_get_uint32_le(const EFI_GUID *vendor, const CHAR16 *name, UINT
UINTN size;
EFI_STATUS err;
assert(vendor);
assert(name);
err = efivar_get_raw(vendor, name, &buf, &size);
if (!EFI_ERROR(err) && ret) {
if (size != sizeof(UINT32))
@ -199,6 +231,9 @@ EFI_STATUS efivar_get_uint64_le(const EFI_GUID *vendor, const CHAR16 *name, UINT
UINTN size;
EFI_STATUS err;
assert(vendor);
assert(name);
err = efivar_get_raw(vendor, name, &buf, &size);
if (!EFI_ERROR(err) && ret) {
if (size != sizeof(UINT64))
@ -217,6 +252,9 @@ EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const CHAR16 *name, CHAR8 **bu
UINTN l;
EFI_STATUS err;
assert(vendor);
assert(name);
l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
buf = AllocatePool(l);
if (!buf)
@ -240,6 +278,10 @@ EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const CHAR16 *name, BOO
UINTN size;
EFI_STATUS err;
assert(vendor);
assert(name);
assert(ret);
err = efivar_get_raw(vendor, name, &b, &size);
if (!EFI_ERROR(err))
*ret = *b > 0;
@ -250,12 +292,15 @@ EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const CHAR16 *name, BOO
VOID efivar_set_time_usec(const EFI_GUID *vendor, const CHAR16 *name, UINT64 usec) {
CHAR16 str[32];
assert(vendor);
assert(name);
if (usec == 0)
usec = time_usec();
if (usec == 0)
return;
SPrint(str, 32, L"%ld", usec);
SPrint(str, ELEMENTSOF(str), L"%ld", usec);
efivar_set(vendor, name, str, 0);
}
@ -263,6 +308,9 @@ static INTN utf8_to_16(const CHAR8 *stra, CHAR16 *c) {
CHAR16 unichar;
UINTN len;
assert(stra);
assert(c);
if (!(stra[0] & 0x80))
len = 1;
else if ((stra[0] & 0xe0) == 0xc0)
@ -316,6 +364,8 @@ CHAR16 *stra_to_str(const CHAR8 *stra) {
UINTN i;
CHAR16 *str;
assert(stra);
len = strlena(stra);
str = AllocatePool((len + 1) * sizeof(CHAR16));
@ -344,6 +394,8 @@ CHAR16 *stra_to_path(const CHAR8 *stra) {
UINTN len;
UINTN i;
assert(stra);
len = strlena(stra);
str = AllocatePool((len + 2) * sizeof(CHAR16));
@ -376,6 +428,7 @@ CHAR16 *stra_to_path(const CHAR8 *stra) {
}
CHAR8 *strchra(const CHAR8 *s, CHAR8 c) {
assert(s);
do {
if (*s == c)
return (CHAR8*) s;
@ -388,6 +441,9 @@ EFI_STATUS file_read(EFI_FILE_HANDLE dir, const CHAR16 *name, UINTN off, UINTN s
_cleanup_freepool_ CHAR8 *buf = NULL;
EFI_STATUS err;
assert(name);
assert(ret);
err = uefi_call_wrapper(dir->Open, 5, dir, &handle, (CHAR16*) name, EFI_FILE_MODE_READ, 0ULL);
if (EFI_ERROR(err))
return err;
@ -425,8 +481,23 @@ EFI_STATUS file_read(EFI_FILE_HANDLE dir, const CHAR16 *name, UINTN off, UINTN s
return err;
}
VOID log_error_stall(const CHAR16 *fmt, ...) {
va_list args;
assert(fmt);
uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTRED|EFI_BACKGROUND_BLACK);
Print(L"\n");
va_start(args, fmt);
VPrint(fmt, args);
va_end(args);
Print(L"\n");
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
}
EFI_STATUS log_oom(void) {
Print(L"Out of memory.");
(void) uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
log_error_stall(L"Out of memory.");
return EFI_OUT_OF_RESOURCES;
}

View File

@ -74,4 +74,13 @@ static inline void FileHandleClosep(EFI_FILE_HANDLE *handle) {
#define UINT64_MAX ((UINT64) -1)
#endif
VOID log_error_stall(const CHAR16 *fmt, ...);
EFI_STATUS log_oom(void);
/* This works just like log_error_errno() from userspace, but requires you
* to provide err a second time if you want to use %r in the message! */
#define log_error_status_stall(err, fmt, ...) \
({ \
log_error_stall(fmt, ##__VA_ARGS__); \
err; \
})

View File

@ -1541,7 +1541,7 @@ Manager* manager_free(Manager *m) {
static void manager_enumerate_perpetual(Manager *m) {
assert(m);
if (m->test_run_flags == MANAGER_TEST_RUN_MINIMAL)
if (FLAGS_SET(m->test_run_flags, MANAGER_TEST_RUN_MINIMAL))
return;
/* Let's ask every type to load all units from disk/kernel that it might know */
@ -1559,7 +1559,7 @@ static void manager_enumerate_perpetual(Manager *m) {
static void manager_enumerate(Manager *m) {
assert(m);
if (m->test_run_flags == MANAGER_TEST_RUN_MINIMAL)
if (FLAGS_SET(m->test_run_flags, MANAGER_TEST_RUN_MINIMAL))
return;
/* Let's ask every type to load all units from disk/kernel that it might know */

View File

@ -133,6 +133,7 @@ typedef enum ManagerTestRunFlags {
MANAGER_TEST_RUN_BASIC = 1 << 1, /* interact with the environment */
MANAGER_TEST_RUN_ENV_GENERATORS = 1 << 2, /* also run env generators */
MANAGER_TEST_RUN_GENERATORS = 1 << 3, /* also run unit generators */
MANAGER_TEST_RUN_IGNORE_DEPENDENCIES = 1 << 4, /* run while ignoring dependencies */
MANAGER_TEST_FULL = MANAGER_TEST_RUN_BASIC | MANAGER_TEST_RUN_ENV_GENERATORS | MANAGER_TEST_RUN_GENERATORS,
} ManagerTestRunFlags;

View File

@ -3053,6 +3053,9 @@ int unit_add_dependency(
return 0;
}
if (u->manager && FLAGS_SET(u->manager->test_run_flags, MANAGER_TEST_RUN_IGNORE_DEPENDENCIES))
return 0;
/* Note that ordering a device unit after a unit is permitted since it allows to start its job
* running timeout at a specific time. */
if (FLAGS_SET(a, UNIT_ATOM_BEFORE) && other->type == UNIT_DEVICE) {
@ -3176,6 +3179,9 @@ int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, boo
if (r < 0)
return r;
if (u->manager && FLAGS_SET(u->manager->test_run_flags, MANAGER_TEST_RUN_IGNORE_DEPENDENCIES))
return 0;
r = manager_load_unit(u->manager, name, NULL, NULL, &other);
if (r < 0)
return r;
@ -3195,6 +3201,9 @@ int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency
if (r < 0)
return r;
if (u->manager && FLAGS_SET(u->manager->test_run_flags, MANAGER_TEST_RUN_IGNORE_DEPENDENCIES))
return 0;
r = manager_load_unit(u->manager, name, NULL, NULL, &other);
if (r < 0)
return r;
@ -3312,6 +3321,9 @@ int unit_set_default_slice(Unit *u) {
assert(u);
if (u->manager && FLAGS_SET(u->manager->test_run_flags, MANAGER_TEST_RUN_IGNORE_DEPENDENCIES))
return 0;
if (UNIT_GET_SLICE(u))
return 0;

View File

@ -14,6 +14,22 @@
#define _used_ __attribute__((__used__))
#define _unused_ __attribute__((__unused__))
#define _cleanup_(x) __attribute__((__cleanup__(x)))
#define _likely_(x) (__builtin_expect(!!(x), 1))
#define _unlikely_(x) (__builtin_expect(!!(x), 0))
#if __GNUC__ >= 7
#define _fallthrough_ __attribute__((__fallthrough__))
#else
#define _fallthrough_
#endif
/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
* compiler versions */
#ifndef _noreturn_
#if __STDC_VERSION__ >= 201112L
#define _noreturn_ _Noreturn
#else
#define _noreturn_ __attribute__((__noreturn__))
#endif
#endif
#define XSTRINGIFY(x) #x
#define STRINGIFY(x) XSTRINGIFY(x)
@ -34,7 +50,14 @@
#define CONCATENATE(x, y) XCONCATENATE(x, y)
#ifdef SD_BOOT
#define assert(expr) do {} while (false)
#ifdef NDEBUG
#define assert(expr)
#define assert_not_reached() __builtin_unreachable()
#else
void efi_assert(const char *expr, const char *file, unsigned line, const char *function) _noreturn_;
#define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); })
#define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __PRETTY_FUNCTION__)
#endif
#endif
#if defined(static_assert)
@ -233,3 +256,10 @@
(ptr) = NULL; \
_ptr_; \
})
/*
* STRLEN - return the length of a string literal, minus the trailing NUL byte.
* Contrary to strlen(), this is a constant expression.
* @x: a string literal.
*/
#define STRLEN(x) (sizeof(""x"") - sizeof(typeof(x[0])))

View File

@ -29,7 +29,7 @@ static int bare_udp_netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *
log_netdev_info(netdev, "BareUDP netdev exists, using existing without changing its parameters.");
else if (r < 0) {
log_netdev_warning_errno(netdev, r, "BareUDP netdev could not be created: %m");
netdev_drop(netdev);
netdev_enter_failed(netdev);
return 1;
}

View File

@ -108,7 +108,7 @@ static int fou_tunnel_create_handler(sd_netlink *rtnl, sd_netlink_message *m, Ne
log_netdev_info(netdev, "netdev exists, using existing without changing its parameters");
else if (r < 0) {
log_netdev_warning_errno(netdev, r, "netdev could not be created: %m");
netdev_drop(netdev);
netdev_enter_failed(netdev);
return 1;
}

View File

@ -37,7 +37,7 @@ static int geneve_netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m,
log_netdev_info(netdev, "Geneve netdev exists, using existing without changing its parameters");
else if (r < 0) {
log_netdev_warning_errno(netdev, r, "Geneve netdev could not be created: %m");
netdev_drop(netdev);
netdev_enter_failed(netdev);
return 1;
}

View File

@ -355,7 +355,7 @@ static int l2tp_create_tunnel_handler(sd_netlink *rtnl, sd_netlink_message *m, N
log_netdev_info(netdev, "netdev exists, using existing without changing its parameters");
else if (r < 0) {
log_netdev_warning_errno(netdev, r, "netdev could not be created: %m");
netdev_drop(netdev);
netdev_enter_failed(netdev);
return 1;
}

View File

@ -317,7 +317,7 @@ static int macsec_receive_association_handler(sd_netlink *rtnl, sd_netlink_messa
else if (r < 0) {
log_netdev_warning_errno(netdev, r,
"Failed to add receive secure association: %m");
netdev_drop(netdev);
netdev_enter_failed(netdev);
return 1;
}
@ -375,7 +375,7 @@ static int macsec_receive_channel_handler(sd_netlink *rtnl, sd_netlink_message *
else if (r < 0) {
log_netdev_warning_errno(netdev, r,
"Failed to add receive secure channel: %m");
netdev_drop(netdev);
netdev_enter_failed(netdev);
return 1;
}
@ -387,7 +387,7 @@ static int macsec_receive_channel_handler(sd_netlink *rtnl, sd_netlink_message *
if (r < 0) {
log_netdev_warning_errno(netdev, r,
"Failed to configure receive security association: %m");
netdev_drop(netdev);
netdev_enter_failed(netdev);
return 1;
}
}
@ -441,7 +441,7 @@ static int macsec_transmit_association_handler(sd_netlink *rtnl, sd_netlink_mess
else if (r < 0) {
log_netdev_warning_errno(netdev, r,
"Failed to add transmit secure association: %m");
netdev_drop(netdev);
netdev_enter_failed(netdev);
return 1;
}

View File

@ -167,6 +167,54 @@ bool netdev_is_managed(NetDev *netdev) {
return hashmap_get(netdev->manager->netdevs, netdev->ifname) == netdev;
}
static bool netdev_is_stacked_and_independent(NetDev *netdev) {
assert(netdev);
if (!IN_SET(netdev_get_create_type(netdev), NETDEV_CREATE_STACKED, NETDEV_CREATE_AFTER_CONFIGURED))
return false;
switch (netdev->kind) {
case NETDEV_KIND_ERSPAN:
return ERSPAN(netdev)->independent;
case NETDEV_KIND_GRE:
return GRE(netdev)->independent;
case NETDEV_KIND_GRETAP:
return GRETAP(netdev)->independent;
case NETDEV_KIND_IP6GRE:
return IP6GRE(netdev)->independent;
case NETDEV_KIND_IP6GRETAP:
return IP6GRETAP(netdev)->independent;
case NETDEV_KIND_IP6TNL:
return IP6TNL(netdev)->independent;
case NETDEV_KIND_IPIP:
return IPIP(netdev)->independent;
case NETDEV_KIND_SIT:
return SIT(netdev)->independent;
case NETDEV_KIND_VTI:
return VTI(netdev)->independent;
case NETDEV_KIND_VTI6:
return VTI6(netdev)->independent;
case NETDEV_KIND_VXLAN:
return VXLAN(netdev)->independent;
case NETDEV_KIND_XFRM:
return XFRM(netdev)->independent;
default:
return false;
}
}
static bool netdev_is_stacked(NetDev *netdev) {
assert(netdev);
if (!IN_SET(netdev_get_create_type(netdev), NETDEV_CREATE_STACKED, NETDEV_CREATE_AFTER_CONFIGURED))
return false;
if (netdev_is_stacked_and_independent(netdev))
return false;
return true;
}
static void netdev_detach_from_manager(NetDev *netdev) {
if (netdev->ifname && netdev->manager)
hashmap_remove(netdev->manager->netdevs, netdev->ifname);
@ -202,9 +250,19 @@ static NetDev *netdev_free(NetDev *netdev) {
DEFINE_TRIVIAL_REF_UNREF_FUNC(NetDev, netdev, netdev_free);
void netdev_drop(NetDev *netdev) {
if (!netdev || netdev->state == NETDEV_STATE_LINGER)
if (!netdev)
return;
if (netdev_is_stacked(netdev)) {
/* The netdev may be removed due to the underlying device removal, and the device may
* be re-added later. */
netdev->state = NETDEV_STATE_LOADING;
netdev->ifindex = 0;
log_netdev_debug(netdev, "netdev removed");
return;
}
netdev->state = NETDEV_STATE_LINGER;
log_netdev_debug(netdev, "netdev removed");
@ -232,9 +290,8 @@ int netdev_get(Manager *manager, const char *name, NetDev **ret) {
return 0;
}
static int netdev_enter_failed(NetDev *netdev) {
void netdev_enter_failed(NetDev *netdev) {
netdev->state = NETDEV_STATE_FAILED;
return 0;
}
static int netdev_enter_ready(NetDev *netdev) {
@ -266,7 +323,7 @@ static int netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev
log_netdev_info(netdev, "netdev exists, using existing without changing its parameters");
else if (r < 0) {
log_netdev_warning_errno(netdev, r, "netdev could not be created: %m");
netdev_drop(netdev);
netdev_enter_failed(netdev);
return 1;
}
@ -560,12 +617,12 @@ static bool netdev_is_ready_to_create(NetDev *netdev, Link *link) {
return true;
}
int request_process_create_stacked_netdev(Request *req) {
int request_process_stacked_netdev(Request *req) {
int r;
assert(req);
assert(req->link);
assert(req->type == REQUEST_TYPE_CREATE_STACKED_NETDEV);
assert(req->type == REQUEST_TYPE_STACKED_NETDEV);
assert(req->netdev);
assert(req->netlink_handler);
@ -632,46 +689,42 @@ static int link_create_stacked_netdev_after_configured_handler(sd_netlink *rtnl,
return 0;
}
int link_request_to_crate_stacked_netdev(Link *link, NetDev *netdev) {
NetDevCreateType create_type;
int link_request_stacked_netdev(Link *link, NetDev *netdev) {
int r;
assert(link);
assert(netdev);
create_type = netdev_get_create_type(netdev);
if (!IN_SET(create_type, NETDEV_CREATE_STACKED, NETDEV_CREATE_AFTER_CONFIGURED))
if (!netdev_is_stacked(netdev))
return -EINVAL;
if (netdev->state != NETDEV_STATE_LOADING || netdev->ifindex > 0)
/* Already created (or removed?) */
return 0;
if (!IN_SET(netdev->state, NETDEV_STATE_LOADING, NETDEV_STATE_FAILED) || netdev->ifindex > 0)
return 0; /* Already created. */
if (create_type == NETDEV_CREATE_STACKED) {
if (netdev_get_create_type(netdev) == NETDEV_CREATE_STACKED) {
link->stacked_netdevs_created = false;
r = link_queue_request(link, REQUEST_TYPE_CREATE_STACKED_NETDEV, netdev, false,
r = link_queue_request(link, REQUEST_TYPE_STACKED_NETDEV, netdev, false,
&link->create_stacked_netdev_messages,
link_create_stacked_netdev_handler,
NULL);
} else {
link->stacked_netdevs_after_configured_created = false;
r = link_queue_request(link, REQUEST_TYPE_CREATE_STACKED_NETDEV, netdev, false,
r = link_queue_request(link, REQUEST_TYPE_STACKED_NETDEV, netdev, false,
&link->create_stacked_netdev_after_configured_messages,
link_create_stacked_netdev_after_configured_handler,
NULL);
}
if (r < 0)
return log_link_error_errno(link, r, "Failed to request to create stacked netdev '%s': %m",
return log_link_error_errno(link, r, "Failed to request stacked netdev '%s': %m",
netdev->ifname);
log_link_debug(link, "Requested to create stacked netdev '%s'", netdev->ifname);
log_link_debug(link, "Requested stacked netdev '%s'", netdev->ifname);
return 0;
}
int netdev_load_one(Manager *manager, const char *filename) {
_cleanup_(netdev_unrefp) NetDev *netdev_raw = NULL, *netdev = NULL;
const char *dropin_dirname;
bool independent = false;
int r;
assert(manager);
@ -792,48 +845,7 @@ int netdev_load_one(Manager *manager, const char *filename) {
return r;
}
switch (netdev->kind) {
case NETDEV_KIND_IPIP:
independent = IPIP(netdev)->independent;
break;
case NETDEV_KIND_GRE:
independent = GRE(netdev)->independent;
break;
case NETDEV_KIND_GRETAP:
independent = GRETAP(netdev)->independent;
break;
case NETDEV_KIND_IP6GRE:
independent = IP6GRE(netdev)->independent;
break;
case NETDEV_KIND_IP6GRETAP:
independent = IP6GRETAP(netdev)->independent;
break;
case NETDEV_KIND_SIT:
independent = SIT(netdev)->independent;
break;
case NETDEV_KIND_VTI:
independent = VTI(netdev)->independent;
break;
case NETDEV_KIND_VTI6:
independent = VTI6(netdev)->independent;
break;
case NETDEV_KIND_IP6TNL:
independent = IP6TNL(netdev)->independent;
break;
case NETDEV_KIND_ERSPAN:
independent = ERSPAN(netdev)->independent;
break;
case NETDEV_KIND_XFRM:
independent = XFRM(netdev)->independent;
break;
case NETDEV_KIND_VXLAN:
independent = VXLAN(netdev)->independent;
break;
default:
break;
}
if (independent) {
if (netdev_is_stacked_and_independent(netdev)) {
r = netdev_create(netdev, NULL, NULL);
if (r < 0)
return r;

View File

@ -184,6 +184,7 @@ extern const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX];
int netdev_load(Manager *manager, bool reload);
int netdev_load_one(Manager *manager, const char *filename);
void netdev_drop(NetDev *netdev);
void netdev_enter_failed(NetDev *netdev);
NetDev *netdev_unref(NetDev *netdev);
NetDev *netdev_ref(NetDev *netdev);
@ -196,8 +197,8 @@ int netdev_set_ifindex(NetDev *netdev, sd_netlink_message *newlink);
int netdev_get_mac(const char *ifname, struct ether_addr **ret);
int netdev_join(NetDev *netdev, Link *link, link_netlink_message_handler_t cb);
int request_process_create_stacked_netdev(Request *req);
int link_request_to_crate_stacked_netdev(Link *link, NetDev *netdev);
int request_process_stacked_netdev(Request *req);
int link_request_stacked_netdev(Link *link, NetDev *netdev);
const char *netdev_kind_to_string(NetDevKind d) _const_;
NetDevKind netdev_kind_from_string(const char *d) _pure_;

View File

@ -594,7 +594,7 @@ static int link_request_stacked_netdevs(Link *link) {
link->stacked_netdevs_after_configured_created = false;
HASHMAP_FOREACH(netdev, link->network->stacked_netdevs) {
r = link_request_to_crate_stacked_netdev(link, netdev);
r = link_request_stacked_netdev(link, netdev);
if (r < 0)
return r;
}

View File

@ -32,8 +32,6 @@ static void request_free_object(RequestType type, void *object) {
case REQUEST_TYPE_BRIDGE_MDB:
bridge_mdb_free(object);
break;
case REQUEST_TYPE_CREATE_STACKED_NETDEV:
break;
case REQUEST_TYPE_DHCP_SERVER:
case REQUEST_TYPE_DHCP4_CLIENT:
case REQUEST_TYPE_DHCP6_CLIENT:
@ -56,6 +54,7 @@ static void request_free_object(RequestType type, void *object) {
routing_policy_rule_free(object);
break;
case REQUEST_TYPE_SET_LINK:
case REQUEST_TYPE_STACKED_NETDEV:
case REQUEST_TYPE_UP_DOWN:
break;
default:
@ -110,7 +109,7 @@ static void request_hash_func(const Request *req, struct siphash *state) {
case REQUEST_TYPE_ADDRESS_LABEL:
case REQUEST_TYPE_BRIDGE_FDB:
case REQUEST_TYPE_BRIDGE_MDB:
case REQUEST_TYPE_CREATE_STACKED_NETDEV:
case REQUEST_TYPE_STACKED_NETDEV:
/* TODO: Currently, these types do not have any specific hash and compare functions.
* Fortunately, all these objects are 'static', thus we can use the trivial functions. */
trivial_hash_func(req->object, state);
@ -173,7 +172,7 @@ static int request_compare_func(const struct Request *a, const struct Request *b
case REQUEST_TYPE_ADDRESS_LABEL:
case REQUEST_TYPE_BRIDGE_FDB:
case REQUEST_TYPE_BRIDGE_MDB:
case REQUEST_TYPE_CREATE_STACKED_NETDEV:
case REQUEST_TYPE_STACKED_NETDEV:
return trivial_compare_func(a->object, b->object);
case REQUEST_TYPE_DHCP_SERVER:
case REQUEST_TYPE_DHCP4_CLIENT:
@ -306,9 +305,6 @@ int manager_process_requests(sd_event_source *s, void *userdata) {
case REQUEST_TYPE_BRIDGE_MDB:
r = request_process_bridge_mdb(req);
break;
case REQUEST_TYPE_CREATE_STACKED_NETDEV:
r = request_process_create_stacked_netdev(req);
break;
case REQUEST_TYPE_DHCP_SERVER:
r = request_process_dhcp_server(req);
break;
@ -339,6 +335,9 @@ int manager_process_requests(sd_event_source *s, void *userdata) {
case REQUEST_TYPE_SET_LINK:
r = request_process_set_link(req);
break;
case REQUEST_TYPE_STACKED_NETDEV:
r = request_process_stacked_netdev(req);
break;
case REQUEST_TYPE_UP_DOWN:
r = request_process_link_up_or_down(req);
break;

View File

@ -26,7 +26,6 @@ typedef enum RequestType {
REQUEST_TYPE_ADDRESS_LABEL,
REQUEST_TYPE_BRIDGE_FDB,
REQUEST_TYPE_BRIDGE_MDB,
REQUEST_TYPE_CREATE_STACKED_NETDEV,
REQUEST_TYPE_DHCP_SERVER,
REQUEST_TYPE_DHCP4_CLIENT,
REQUEST_TYPE_DHCP6_CLIENT,
@ -37,6 +36,7 @@ typedef enum RequestType {
REQUEST_TYPE_ROUTE,
REQUEST_TYPE_ROUTING_POLICY_RULE,
REQUEST_TYPE_SET_LINK,
REQUEST_TYPE_STACKED_NETDEV,
REQUEST_TYPE_UP_DOWN,
_REQUEST_TYPE_MAX,
_REQUEST_TYPE_INVALID = -EINVAL,

View File

@ -362,10 +362,9 @@ void dns_server_packet_rcode_downgrade(DnsServer *s, DnsServerFeatureLevel level
if (s->possible_feature_level > level) {
s->possible_feature_level = level;
dns_server_reset_counters(s);
}
log_debug("Downgrading transaction feature level fixed an RCODE error, downgrading server %s too.", strna(dns_server_string_full(s)));
}
}
void dns_server_packet_invalid(DnsServer *s, DnsServerFeatureLevel level) {
assert(s);

View File

@ -1142,6 +1142,18 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypt
break;
}
/* SERVFAIL can happen for many reasons and may be transient.
* To avoid unnecessary downgrades retry once with the initial level.
* Check for clamp_feature_level_servfail having an invalid value as a sign that this is the
* first attempt to downgrade. If so, clamp to the current value so that the transaction
* is retried without actually downgrading. If the next try also fails we will downgrade by
* hitting the else branch below. */
if (DNS_PACKET_RCODE(p) == DNS_RCODE_SERVFAIL &&
t->clamp_feature_level_servfail < 0) {
t->clamp_feature_level_servfail = t->current_feature_level;
log_debug("Server returned error %s, retrying transaction.",
dns_rcode_to_string(DNS_PACKET_RCODE(p)));
} else {
/* Reduce this feature level by one and try again. */
switch (t->current_feature_level) {
case DNS_SERVER_FEATURE_LEVEL_TLS_DO:
@ -1158,6 +1170,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypt
log_debug("Server returned error %s, retrying transaction with reduced feature level %s.",
dns_rcode_to_string(DNS_PACKET_RCODE(p)),
dns_server_feature_level_to_string(t->clamp_feature_level_servfail));
}
dns_transaction_retry(t, false /* use the same server */);
return;

View File

@ -7,6 +7,8 @@
/* Do some basic checks on STRLEN() and DECIMAL_STR_MAX() */
assert_cc(STRLEN("xxx") == 3);
assert_cc(STRLEN("") == 0);
assert_cc(STRLEN(L"xxx") == 3 * sizeof(wchar_t));
assert_cc(STRLEN(L"") == 0);
assert_cc(DECIMAL_STR_MAX(uint8_t) == 5);
assert_cc(DECIMAL_STR_MAX(int8_t) == 5);
assert_cc(DECIMAL_STR_MAX(uint64_t) == 22);

View File

@ -1183,6 +1183,25 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
self.assertRegex(output, ' mtu 2000 ')
self.assertRegex(output, 'macvlan mode ' + mode + ' ')
rc = call("ip link del test1")
self.assertEqual(rc, 0)
time.sleep(1)
rc = call("ip link add test1 type dummy")
self.assertEqual(rc, 0)
time.sleep(1)
self.wait_online(['macvlan99:degraded', 'test1:degraded'])
output = check_output('ip -d link show test1')
print(output)
self.assertRegex(output, ' mtu 2000 ')
output = check_output('ip -d link show macvlan99')
print(output)
self.assertRegex(output, ' mtu 2000 ')
self.assertRegex(output, 'macvlan mode ' + mode + ' ')
@expectedFailureIfModuleIsNotAvailable('ipvlan')
def test_ipvlan(self):
for mode, flag in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]: