Compare commits

...

25 Commits

Author SHA1 Message Date
Dan Streetman 39c5ed0c91
Merge 413f6e0f56 into 7ac1ad90d0 2024-09-17 20:58:45 -07:00
Yu Watanabe 7ac1ad90d0
Merge pull request #34460 from yuwata/test-86-follow-ups
test: follow-ups for TEST-86
2024-09-18 09:31:17 +09:00
Daan De Meyer 099b16c3e7 tmpfiles.d: Remove purge flag from lines that don't support it
Fixes db15657dfb
2024-09-17 23:02:01 +02:00
Daan De Meyer 7a7f306b6c ukify: Remove debug log
This prints a python data structure which we shouldn't do during
normal operation.
2024-09-17 22:34:13 +02:00
Yu Watanabe 4f2975385f
Merge pull request #34040 from AdrianVovk/repart-dollar-boot
repart: Implement $BOOT support
2024-09-18 05:09:20 +09:00
Daan De Meyer 0432e28394
Merge pull request #34440 from yuwata/network-log-no-matching-network
network: log when no matching .network file found
2024-09-17 21:09:19 +02:00
Yu Watanabe fc956a3973 network/dhcp4: use device_get_property_bool() at link_needs_dhcp_broadcast()
No functional change, just refactoring.
2024-09-17 21:03:59 +02:00
Yu Watanabe d265b8afb7 test: drop unused test.sh for TEST-86-MULTI-PROFILE-UKI
The test cannot run with the bash test runner, as it requires python.
Hence, test.sh is not necessary.

Follow-up for a37640653c.
2024-09-18 04:00:05 +09:00
Yu Watanabe 1aab0a5b10 test: minor coding style fixlets
Follow-up for a37640653c.
2024-09-18 03:50:46 +09:00
Yu Watanabe b0dbb4aa3a
Merge pull request #34457 from poettering/uki-with-many-testcase
multi-profile UKIs: test case
2024-09-18 03:48:45 +09:00
Michael Ferrari 91ea3dcf35 homed: wait for user input during firstboot
This mirrors the behavior of `systemd-firstboot` and allows bootup
messages to settle down before user input is actually processed.

See: https://github.com/systemd/systemd/issues/34448
2024-09-18 03:21:11 +09:00
Yu Watanabe a95ae2d36a conf-parser: use hashmap_ensure_put() at one more place 2024-09-18 03:13:47 +09:00
Yu Watanabe be8e4b1a87 conf-parser: log errors in config_parse_many_files() and friends
Previously, if an file cannot be opened, e.g. due to its permission,
config_parse_many() or so did not log the error even if CONFIG_PARSE_WARN
flag is set. This makes all error paths in these functions are logged,
and the log level is controlled by the flag.

Prompted by #34436.
2024-09-18 03:13:25 +09:00
Adrian Vovk cf612c5fd5
repart: Add tests for supplement partitions 2024-09-17 14:06:51 -04:00
Adrian Vovk 2cb9c68c3a
repart: Add SupplementFor= logic
This was designed to deal with $BOOT, as defined by the Boot Loader
Specification, but it was made a generic mechanism because it is useful
elsewhere too. See the updated man page for usage examples, motivation,
and an explanation of how this works.
2024-09-17 14:06:50 -04:00
Adrian Vovk 78e9059208
repart: Consider existing partitions when placing
Fixes an oversight in `context_allocate_partitions` that makes it
succeed in cases where it should fail. Essentially, there was nothing
actually enforcing SizeMinBytes= and PaddingMinBytes= for partitions
that exist, only for new partitions. This behavior is inconsistent with
the docs, which state that existing partitions will be grown to at least
the specified minimum size, and that "If the backing device does not
provide enough space to fulfill the constraints placing the partition
will fail".
2024-09-17 14:06:49 -04:00
Adrian Vovk e671bdc5c3
strv: Fixup STRV_FOREACH_PAIR macro
The macro didn't properly parenthesize a caller-controlled argument.
For example: `STRV_FOREACH_PAIR(a, b, something ?: something_else)`
would expand to `typeof(*something ?: something_else)`, which would
cause compile failures
2024-09-17 14:06:26 -04:00
Yu Watanabe 572d031eca log: introduce log_oom_full() 2024-09-18 02:50:19 +09:00
Yu Watanabe 25da422bd1 network: log loaded .network and .netdev files 2024-09-18 02:35:28 +09:00
Yu Watanabe 5872ea7008 network: log when no matching .network file found
When an interface enters unmanaged state, there are two possibilities:
- no matching .network file found,
- found a matching .network with Unmanaged=yes.

When a matching .network file is found, networkd logs the filename.
Let's also log when no matching .network file is found.

This also slightly adjust the log message when a matching .network file
found.

Closes #34436.
2024-09-18 02:27:13 +09:00
Lennart Poettering a2369d0224 update TODO 2024-09-17 10:40:51 +02:00
Lennart Poettering a37640653c ci: add testcase for multi-profile UKIs
This tests the whole shebang:

1. That ukify can generate them properly
2. That systemd-boot can dissect them properly
3. That systemd-stub can accept profile selection propery
4. That the profile information ends up in /run/systemd/stub/ properly
5. That systemd-measure correctly calculates the expected PCR 11 values
   for each profile and that we can unlock a public-key bound LUKS
   volume with it
2024-09-17 10:40:51 +02:00
Dan Streetman 413f6e0f56 test/test-variadic: add unit tests for variadic helper macros 2023-05-01 14:14:18 -04:00
Dan Streetman f3ab293474 fundamental/variadic-fundamental: add variadic helper macros
Adds VA_MACRO_HELPER() which allows calling another macro with temporary
variables, unique tokens, and/or existing variables protected from side
effects.

Also adds various other helper macros for dealing with variadic args, such as
VA_GROUP(), VA_IF(), VA_FIRST(), etc.
2023-05-01 14:14:18 -04:00
Dan Streetman b595c40c72 fundamental/macro-fundamental: allow stringifying variadic args, instead of just a single arg 2023-05-01 12:58:34 -04:00
26 changed files with 1986 additions and 146 deletions

2
TODO
View File

@ -189,6 +189,8 @@ Features:
* go through our codebase, and convert "vertical tables" (i.e. things such as
"systemctl status") to use table_new_vertical() for output
* pcrlock: add support for multi-profile UKIs
* logind: when logging in use new tmpfs quota support to configure quota on
/tmp/ + /dev/shm/. But do so only in case of tmpfs, because otherwise quota
is persistent and any persistent settings mean we don#t have to reapply them.

View File

@ -76,16 +76,7 @@
<term><varname>Type=</varname></term>
<listitem><para>The GPT partition type UUID to match. This may be a GPT partition type UUID such as
<constant>4f68bce3-e8cd-4db1-96e7-fbcaf984b709</constant>, or an identifier.
Architecture specific partition types can use one of these architecture identifiers:
<constant>alpha</constant>, <constant>arc</constant>, <constant>arm</constant> (32-bit),
<constant>arm64</constant> (64-bit, aka aarch64), <constant>ia64</constant>,
<constant>loongarch64</constant>, <constant>mips-le</constant>, <constant>mips64-le</constant>,
<constant>parisc</constant>, <constant>ppc</constant>, <constant>ppc64</constant>,
<constant>ppc64-le</constant>, <constant>riscv32</constant>, <constant>riscv64</constant>,
<constant>s390</constant>, <constant>s390x</constant>, <constant>tilegx</constant>,
<constant>x86</constant> (32-bit, aka i386) and <constant>x86-64</constant> (64-bit, aka amd64).
</para>
<constant>4f68bce3-e8cd-4db1-96e7-fbcaf984b709</constant>, or an identifier.</para>
<para>The supported identifiers are:</para>
@ -237,7 +228,14 @@
</tgroup>
</table>
<para>This setting defaults to <constant>linux-generic</constant>.</para>
<para>Architecture specific partition types can use one of these architecture identifiers:
<constant>alpha</constant>, <constant>arc</constant>, <constant>arm</constant> (32-bit),
<constant>arm64</constant> (64-bit, aka aarch64), <constant>ia64</constant>,
<constant>loongarch64</constant>, <constant>mips-le</constant>, <constant>mips64-le</constant>,
<constant>parisc</constant>, <constant>ppc</constant>, <constant>ppc64</constant>,
<constant>ppc64-le</constant>, <constant>riscv32</constant>, <constant>riscv64</constant>,
<constant>s390</constant>, <constant>s390x</constant>, <constant>tilegx</constant>,
<constant>x86</constant> (32-bit, aka i386) and <constant>x86-64</constant> (64-bit, aka amd64).</para>
<para>Most of the partition type UUIDs listed above are defined in the <ulink
url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification">Discoverable Partitions
@ -897,6 +895,59 @@
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>SupplementFor=</varname></term>
<listitem><para>Takes a partition definition name, such as <literal>10-esp</literal>. If specified,
<command>systemd-repart</command> will avoid creating this partition and instead prefer to partially
merge the two definitions. However, depending on the existing layout of partitions on disk,
<command>systemd-repart</command> may be forced to fall back onto un-merging the definitions and
using them as originally written, potentially creating this partition. Specifically,
<command>systemd-repart</command> will fall back if this partition is found to already exist on disk,
or if the target partition already exists on disk but is too small, or if it cannot allocate space
for the merged partition for some other reason.</para>
<para>The following fields are merged into the target definition in the specified ways:
<varname>Weight=</varname> and <varname>PaddingWeight=</varname> are simply overwritten;
<varname>SizeMinBytes=</varname> and <varname>PaddingMinBytes=</varname> use the larger of the two
values; <varname>SizeMaxBytes=</varname> and <varname>PaddingMaxBytes=</varname> use the smaller
value; and <varname>CopyFiles=</varname>, <varname>ExcludeFiles=</varname>,
<varname>ExcludeFilesTarget=</varname>, <varname>MakeDirectories=</varname>, and
<varname>Subvolumes=</varname> are concatenated.</para>
<para>Usage of this option in combination with <varname>CopyBlocks=</varname>,
<varname>Encrypt=</varname>, or <varname>Verity=</varname> is not supported. The target definition
cannot set these settings either. A definition cannot simultaneously be a supplement and act as a
target for some other supplement definition. A target cannot have more than one supplement partition
associated with it.</para>
<para>For example, distributions can use this to implement <variable>$BOOT</variable> as defined in
the <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification/">Boot Loader
Specification</ulink>. Distributions may prefer to use the ESP as <variable>$BOOT</variable> whenever
possible, but to adhere to the spec XBOOTLDR must sometimes be used instead. So, they should create
two definitions: the first defining an ESP big enough to hold just the bootloader, and a second for
the XBOOTLDR that's sufficiently large to hold kernels and configured as a supplement for the ESP.
Whenever possible, <command>systemd-repart</command> will try to merge the two definitions to create
one large ESP, but if that's not allowable due to the existing conditions on disk a small ESP and a
large XBOOTLDR will be created instead.</para>
<para>As another example, distributions can also use this to seamlessly share a single
<filename>/home</filename> partition in a multi-boot scenario, while preferring to keep
<filename>/home</filename> on the root partition by default. Having a <filename>/home</filename>
partition separated from the root partition entails some extra complexity: someone has to decide how
to split the space between the two partitions. On the other hand, it allows a user to share their
home area between multiple installed OSs (i.e. via <citerefentry><refentrytitle>systemd-homed.service
</refentrytitle><manvolnum>8</manvolnum></citerefentry>). Distributions should create two definitions:
the first for a root partition that takes up some relatively small percentage of the disk, and the
second as a supplement for the first to create a <filename>/home</filename> partition that takes up
all the remaining free space. On first boot, if <command>systemd-repart</command> finds an existing
<filename>/home</filename> partition on disk, it'll un-merge the definitions and create just a small
root partition. Otherwise, the definitions will be merged and a single large root partition will be
created.</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@ -300,9 +300,10 @@ int log_emergency_level(void);
#define log_dump(level, buffer) \
log_dump_internal(level, 0, PROJECT_FILE, __LINE__, __func__, buffer)
#define log_oom() log_oom_internal(LOG_ERR, PROJECT_FILE, __LINE__, __func__)
#define log_oom_debug() log_oom_internal(LOG_DEBUG, PROJECT_FILE, __LINE__, __func__)
#define log_oom_warning() log_oom_internal(LOG_WARNING, PROJECT_FILE, __LINE__, __func__)
#define log_oom_full(level) log_oom_internal(level, PROJECT_FILE, __LINE__, __func__)
#define log_oom() log_oom_full(LOG_ERR)
#define log_oom_debug() log_oom_full(LOG_DEBUG)
#define log_oom_warning() log_oom_full(LOG_WARNING)
bool log_on_console(void) _pure_;

View File

@ -153,7 +153,7 @@ bool strv_overlap(char * const *a, char * const *b) _pure_;
_STRV_FOREACH_BACKWARDS(s, l, UNIQ_T(h, UNIQ), UNIQ_T(i, UNIQ))
#define _STRV_FOREACH_PAIR(x, y, l, i) \
for (typeof(*l) *x, *y, *i = (l); \
for (typeof(*(l)) *x, *y, *i = (l); \
i && *(x = i) && *(y = i + 1); \
i += 2)

View File

@ -255,6 +255,25 @@ int ask_string(char **ret, const char *text, ...) {
return 0;
}
bool any_key_to_proceed(void) {
char key = 0;
bool need_nl = true;
/*
* Insert a new line here as well as to when the user inputs, as this is also used during the
* boot up sequence when status messages may be interleaved with the current program output.
* This ensures that the status messages aren't appended on the same line as this message.
*/
puts("-- Press any key to proceed --");
(void) read_one_char(stdin, &key, USEC_INFINITY, &need_nl);
if (need_nl)
putchar('\n');
return key != 'q';
}
int open_terminal(const char *name, int mode) {
_cleanup_close_ int fd = -EBADF;
unsigned c = 0;

View File

@ -78,6 +78,7 @@ int chvt(int vt);
int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl);
int ask_char(char *ret, const char *replies, const char *text, ...) _printf_(3, 4);
int ask_string(char **ret, const char *text, ...) _printf_(2, 3);
bool any_key_to_proceed(void);
int vt_disallocate(const char *name);

View File

@ -93,20 +93,6 @@ STATIC_DESTRUCTOR_REGISTER(arg_root_shell, freep);
STATIC_DESTRUCTOR_REGISTER(arg_kernel_cmdline, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
static bool press_any_key(void) {
char k = 0;
bool need_nl = true;
puts("-- Press any key to proceed --");
(void) read_one_char(stdin, &k, USEC_INFINITY, &need_nl);
if (need_nl)
putchar('\n');
return k != 'q';
}
static void print_welcome(int rfd) {
_cleanup_free_ char *pretty_name = NULL, *os_name = NULL, *ansi_color = NULL;
static bool done = false;
@ -141,7 +127,7 @@ static void print_welcome(int rfd) {
printf("\nPlease configure your system!\n\n");
press_any_key();
any_key_to_proceed();
done = true;
}
@ -184,7 +170,7 @@ static int show_menu(char **x, unsigned n_columns, unsigned width, unsigned perc
/* on the first screen we reserve 2 extra lines for the title */
if (i % break_lines == break_modulo) {
if (!press_any_key())
if (!any_key_to_proceed())
return 0;
}
}

View File

@ -86,8 +86,8 @@
# define _fallthrough_
#endif
#define XSTRINGIFY(x) #x
#define STRINGIFY(x) XSTRINGIFY(x)
#define XSTRINGIFY(...) #__VA_ARGS__
#define STRINGIFY(...) XSTRINGIFY(__VA_ARGS__)
#ifndef __COVERITY__
# define VOID_0 ((void)0)

View File

@ -0,0 +1,555 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
/* This contains macros that all help simplify the use of macros with variadic args. Also provided is a macro
* 'helper' that helps provide some commonly used things, such as a unique variable name or temporary
* variable.
*
* Since the C preprocessor does not allow recursive macros, none of these macros may be used to call
* themselves, even indirectly (except by using a "trick"; e.g. see __VA_WRAP_RECURSE() below). If you get a
* compiler error complaining about "implicit declaration of function" for any of the macros here, it is most
* likely due to an attempt to use the macro recursively.
*
* Some macros operate based on if there are 'any variadic args' or 'no variadic args'; this distinction is
* based on the use of __VA_OPT__(). The description 'any variadic args' means __VA_OPT__() evaluates to its
* content, and 'no variadic args' means __VA_OPT__() evaluates to nothing. Note that whitespace is not a
* preprocessor token, so a single whitespace-only arg is the same as no args. For example these calls all
* evaluate to 2:
* VA_IF_ELSE(1,2)
* VA_IF_ELSE(1,2,)
* VA_IF_ELSE(1,2, )
* #define NONE
* VA_IF_ELSE(1,2,NONE)
* VA_IF_ELSE(1,2, NONE)
* However, this call evaluates to 1:
* VA_IF_ELSE(1,2,,)
*/
/* Wraps variadic args in a single group. This can be passed to macros that will then expand the group into
* all its variadic args. */
#define VA_GROUP(...) __VA_ARGS__
/* Evaluates to 'x' if any variadic args, otherwise 'y'. */
#define VA_IF_ELSE(x, y, ...) _VA_IF_ELSE_MACRO(__VA_ARGS__)(_VA_IF_ELSE_GROUP(x), _VA_IF_ELSE_GROUP(y))
#define _VA_IF_ELSE_GROUP(...) __VA_ARGS__
#define _VA_IF_ELSE_MACRO(...) __VA_IF_ELSE_MACRO(__VA_OPT__(1))
#define __VA_IF_ELSE_MACRO(o) _VA_IF_ELSE ## o
#define _VA_IF_ELSE1(x, y) x
#define _VA_IF_ELSE(x, y) y
/* Evaluates to nothing if no variadic args, otherwise 'x'. */
#define VA_IF(x, ...) VA_IF_ELSE(_VA_IF_GROUP(x), /*false*/, __VA_ARGS__)
#define _VA_IF_GROUP(...) __VA_ARGS__
/* Same as VA_IF() but negates the condition. */
#define VA_IF_NOT(x, ...) VA_IF_ELSE(/*true*/, _VA_IF_NOT_GROUP(x), __VA_ARGS__)
#define _VA_IF_NOT_GROUP(...) __VA_ARGS__
/* Evaluates to token 1 if no variadic args, otherwise nothing. */
#define VA_NOT(...) VA_IF_NOT(1, __VA_ARGS__)
/* Evaluates to the first variadic arg, otherwise nothing. */
#define VA_FIRST(...) VA_IF(_VA_FIRST(__VA_ARGS__), __VA_ARGS__)
#define _VA_FIRST(x, ...) x
/* Evaluates to the rest of the variadic args, after the first, otherwise nothing. */
#define VA_REST(...) VA_IF(_VA_REST(__VA_ARGS__), __VA_ARGS__)
#define _VA_REST(x, ...) __VA_ARGS__
/* Evaluates to token , if any variadic args, otherwise nothing. */
#define VA_COMMA(...) __VA_OPT__(,)
/* Evaluates to token 1 if both args are non-empty (ignoring whitespace), otherwise evaluates to nothing. */
#define VA_AND(x, y) VA_NOT(VA_NOT(x) VA_NOT(y))
/* Evaluates to token 1 if either arg is non-empty (ignoring whitespace), otherwise evaluates to nothing. */
#define VA_OR(x, y) VA_IF(1, x y)
/* Evaluates to nothing. */
#define VA_NOOP(...)
/* Similar to VA_GROUP(), but encloses the variadic args in (), so they are not expanded when passed to other
* macros. Unlike VA_GROUP(), this requires the final macro that actually uses the group contents to ungroup it
* using VA_UNPGROUP(), or to handle the () directly. */
#define VA_PGROUP(...) (__VA_ARGS__)
/* Converts a group of args protected with () into a normal VA_GROUP(). 'x' must be a pgroup, i.e. (...). */
#define VA_UNPGROUP(x) VA_GROUP x
/* Similar to VA_FIRST(), but x is a pgroup. Evaluates to the first arg if present, otherwise nothing. */
#define VA_PGROUP_FIRST(x) VA_FIRST(VA_UNPGROUP(x))
/* Similar to VA_REST(), but x is a pgroup. Evaluates to a pgroup of the args after the first. If there are
* no more args after the first, evaluates to an empty pgroup. */
#define VA_PGROUP_REST(x) VA_PGROUP(VA_REST(VA_UNPGROUP(x)))
/* Evaluates to token 1 if pgroup is empty, otherwise nothing. */
#define VA_PGROUP_EMPTY(x) VA_IF_NOT(1, VA_UNPGROUP(x))
/* Similar to VA_PGROUP_EMPTY() but negates the condition. */
#define VA_PGROUP_NOT_EMPTY(x) VA_NOT(VA_PGROUP_EMPTY(x))
/* Evaluates to 'macro' called with the expanded variadic args. */
#define VA_MACRO(macro, ...) macro(__VA_ARGS__)
/* Evaluates to x(__VA_ARGS__) if t is non-empty, otherwise y(__VA_ARGS__). */
#define VA_MACRO_IF_ELSE(x, y, t, ...) VA_IF_ELSE(x, y, t)(__VA_ARGS__)
/* Evaluates to m(__VA_ARGS__) if t is non-empty, otherwise nothing. */
#define VA_MACRO_IF(m, t, ...) VA_MACRO_IF_ELSE(m, VA_NOOP, t, __VA_ARGS__)
/* Evaluates to m(__VA_ARGS__) if t is empty, otherwise nothing. */
#define VA_MACRO_IF_NOT(m, t, ...) VA_MACRO_IF_ELSE(VA_NOOP, m, t, __VA_ARGS__)
/* Same as VA_MACRO() but takes a pgroup, which is unpgrouped before passing to the macro. */
#define VA_MACRO_PGROUP(macro, pgroup) VA_MACRO(macro, VA_UNPGROUP(pgroup))
/* Expands to 'macro' for each variadic arg, which will be called with:
* 1) the provided 'context'
* 2) a hex iteration number (starting at 0x0001)
* 3) the variadic arg
* 4...) the rest of the variadic args
*
* Each expansion of 'macro', except for the last, will be followed by 'separator' called with the same
* parameters as 'macro'.
*
* If there are no variadic args, this evaluates to 'zero' called with the single arg 'context'.
*
* If there are too many variadic args, this evaluates to 'toomany' called with the single arg 'context'.
*
* The 'macro', 'separator', 'zero', and 'toomany' parameters must be callable macros. The VA_WRAP_*() macros
* below may be used. The 'context' parameter may be anything and is not directly called (except by the
* VA_WRAP_*_CONTEXT() below). */
#define VA_WRAP(macro, separator, context, zero, toomany, ...) \
__VA_WRAP_RECURSE(macro, separator, context, zero, toomany, __VA_ARGS__)
/* These can be used for the VA_WRAP() 'macro' parameter. */
#define VA_WRAP_MACRO_CONTEXT(c, i, v, ...) c
#define VA_WRAP_MACRO_INDEX(c, i, v, ...) i
#define VA_WRAP_MACRO_LAST(c, i, v, ...) VA_IF_NOT(v, __VA_ARGS__)
#define VA_WRAP_MACRO_LAST_INDEX(c, i, v, ...) VA_IF_NOT(i, __VA_ARGS__)
#define VA_WRAP_MACRO_NONE(c, i, v, ...)
#define VA_WRAP_MACRO_VALUE(c, i, v, ...) v
/* These can be used for the VA_WRAP() 'separator' parameter. */
#define VA_WRAP_SEPARATOR_AND(c, i, v, ...) &&
#define VA_WRAP_SEPARATOR_COMMA(c, i, v, ...) ,
#define VA_WRAP_SEPARATOR_COMMA_IF_PREV(c, i, v, ...) VA_COMMA(v)
#define VA_WRAP_SEPARATOR_CONTEXT(c, i, v, ...) c
#define VA_WRAP_SEPARATOR_INDEX(c, i, v, ...) i
#define VA_WRAP_SEPARATOR_NONE(c, i, v, ...)
#define VA_WRAP_SEPARATOR_SEMICOLON(c, i, v, ...) ;
/* This can be used for the VA_WRAP() 'context' parameter. It is strictly to help with code readability, and
* is not required. */
#define VA_WRAP_CONTEXT_NONE
/* These can be used for the VA_WRAP() 'zero' parameter. */
#define VA_WRAP_ZERO_0(c) 0
#define VA_WRAP_ZERO_0x0000(c) 0x0000
#define VA_WRAP_ZERO_CONTEXT(c) c
#define VA_WRAP_ZERO_ERROR(c) _Pragma("GCC error \"Zero variadic args.\"")
#define VA_WRAP_ZERO_FALSE(c) false
#define VA_WRAP_ZERO_NONE(c)
#define VA_WRAP_ZERO_TRUE(c) true
#define VA_WRAP_ZERO_VOID_0(c) VOID_0
/* These can be used for the VA_WRAP() 'toomany' parameter. */
#define VA_WRAP_TOOMANY_CONTEXT(c) c
#define VA_WRAP_TOOMANY_ERROR(c) _Pragma("GCC error \"Too many variadic args.\"")
#define VA_WRAP_TOOMANY_FALSE(c) false
#define VA_WRAP_TOOMANY_NONE(c)
#define VA_WRAP_TOOMANY_TRUE(c) true
/* Simple version of VA_WRAP(); each variadic arg is wrapped by the provided macro, separated by commas. No
* context is used. Zero args evaluates to nothing. Toomany args results in error. */
#define VA_MACRO_FOREACH(macro, ...) \
VA_WRAP(macro, \
VA_WRAP_SEPARATOR_COMMA, \
VA_WRAP_CONTEXT_NONE, \
VA_WRAP_ZERO_NONE, \
VA_WRAP_TOOMANY_ERROR, \
__VA_ARGS__)
/* Expands to list of variadic args, with any "empty" (whitespace only) args removed. This processes the list
* twice, to remove a trailing comma if needed. */
#define VA_FILTER(...) \
VA_MACRO(VA_WRAP, \
VA_WRAP_MACRO_VALUE, \
VA_WRAP_SEPARATOR_COMMA_IF_PREV, \
VA_WRAP_CONTEXT_NONE, \
VA_WRAP_ZERO_NONE, \
VA_WRAP_TOOMANY_ERROR, \
VA_WRAP(VA_WRAP_MACRO_VALUE, \
VA_WRAP_SEPARATOR_COMMA_IF_PREV, \
VA_WRAP_CONTEXT_NONE, \
VA_WRAP_ZERO_NONE, \
VA_WRAP_TOOMANY_ERROR, \
__VA_ARGS__))
/* Evaluates to the number of variadic args. */
#define VA_NARGS(...) \
VA_WRAP(VA_WRAP_MACRO_LAST_INDEX, \
VA_WRAP_SEPARATOR_NONE, \
VA_WRAP_CONTEXT_NONE, \
VA_WRAP_ZERO_0x0000, \
VA_WRAP_TOOMANY_ERROR, \
__VA_ARGS__)
/* Evaluates to the last variadic arg. If there are no variadic args, evaluates to nothing. */
#define VA_LAST(...) \
VA_WRAP(VA_WRAP_MACRO_LAST, \
VA_WRAP_SEPARATOR_NONE, \
VA_WRAP_CONTEXT_NONE, \
VA_WRAP_ZERO_NONE, \
VA_WRAP_TOOMANY_ERROR, \
__VA_ARGS__)
#define _VA_DECLARATIONS(macro, name, ...) \
VA_WRAP(macro, \
VA_WRAP_SEPARATOR_SEMICOLON, \
name, \
VA_WRAP_ZERO_NONE, \
VA_WRAP_TOOMANY_ERROR, \
__VA_ARGS__)
#define _VA_DECLARATION_TOKEN(x, y) __VA_DECLARATION_TOKEN(x, y)
#define __VA_DECLARATION_TOKEN(x, y) x ## _ ## y
/* Evaluates to a variable declaration for each variadic arg. Each variadic arg must be a type. Each variable
* name is the concatenation of 'name', '_', and the variadic arg index (as a hex number). */
#define VA_DECLARATIONS(name, ...) \
_VA_DECLARATIONS(_VA_DECLARATION, name, __VA_ARGS__)
#define _VA_DECLARATION(c, i, v, ...) \
v _VA_DECLARATION_TOKEN(c, i)
/* Same as VA_DECLARATIONS(), but the variadic args must be variables (or constants). Each declaration
* uses __auto_type and is initialized to its corresponding variadic arg. */
#define VA_INITIALIZED_DECLARATIONS(name, ...) \
_VA_DECLARATIONS(_VA_INITIALIZED_DECLARATION, name, __VA_ARGS__)
#define _VA_INITIALIZED_DECLARATION(c, i, v, ...) \
_VA_DECLARATION(c, i, __auto_type, __VA_ARGS__) = (v)
/* Same as VA_INITIALIZED_DECLARATIONS(), but the temp variable is declared with const. */
#define VA_CONST_INITIALIZED_DECLARATIONS(name, ...) \
_VA_DECLARATIONS(_VA_CONST_INITIALIZED_DECLARATION, name, __VA_ARGS__)
#define _VA_CONST_INITIALIZED_DECLARATION(c, i, v, ...) \
const _VA_INITIALIZED_DECLARATION(c, i, v, __VA_ARGS__)
/* Evaluates to a comma-separated list of tokens by concatenating 'name' and a literal '_' with each variadic
* arg index. This will produce the same tokens as the variable names generated by VA_DECLARATIONS(). Note
* this does not actually evaluate any of the variadic args. */
#define VA_TOKENS(name, ...) \
VA_WRAP(_VA_TOKEN, \
VA_WRAP_SEPARATOR_COMMA, \
name, \
VA_WRAP_ZERO_NONE, \
VA_WRAP_TOOMANY_ERROR, \
__VA_ARGS__)
#define _VA_TOKEN(c, i, v, ...) _VA_DECLARATION_TOKEN(c, i)
/* Evaluates to a comma-separated list of unique tokens using UNIQ_T() for each variadic arg. This is similar
* to VA_TOKENS() but uses UNIQ_T() to generate the tokens. */
#define VA_UNIQ(...) \
VA_WRAP(_VA_UNIQ, \
VA_WRAP_SEPARATOR_COMMA, \
UNIQ, \
VA_WRAP_ZERO_NONE, \
VA_WRAP_TOOMANY_ERROR, \
__VA_ARGS__)
#define _VA_UNIQ(c, i, v, ...) UNIQ_T(v, c)
/* This is similar to VA_FILTER(), but we can't use VA_FILTER() because macros can't be used recursively, and
* this is called from inside a VA_WRAP() (which VA_FILTER() relies on). */
#define __VMH_GROUPS(g1, g2, g3, g4, g5) \
g1 VA_IF(VA_COMMA(g1), g2 g3 g4 g5) \
g2 VA_IF(VA_COMMA(g2), g3 g4 g5) \
g3 VA_IF(VA_COMMA(g3), g4 g5) \
g4 VA_IF(VA_COMMA(g4), g5) \
g5
#define __VMH_TOKEN(x, u) __va_macro_helper ## x ## u
#define __VMH_STATEMENT_EXPRESSION(macro, u, uniq, var, varinit, varconst, direct) \
({ \
VA_DECLARATIONS( __VMH_TOKEN(_var_, u), var); \
VA_INITIALIZED_DECLARATIONS( __VMH_TOKEN(_varinit_, u), varinit); \
VA_CONST_INITIALIZED_DECLARATIONS(__VMH_TOKEN(_varconst_, u), varconst); \
VA_MACRO(macro, \
__VMH_GROUPS(VA_UNIQ(uniq), \
VA_TOKENS(__VMH_TOKEN(_var_, u), var), \
VA_TOKENS(__VMH_TOKEN(_varinit_, u), varinit), \
VA_TOKENS(__VMH_TOKEN(_varconst_, u), varconst), \
VA_GROUP(direct))); \
})
#define __VMH_EXPRESSION(macro, u, uniq, var, varinit, varconst, direct) \
VA_MACRO(macro, \
__VMH_GROUPS(VA_UNIQ(uniq), VA_GROUP(direct),,,))
/* Calls 'macro' with a set of args based on the provided arg groups, in the order shown. Multiple args may
* be provided to each group by using VA_GROUP().
*
* Each arg in the 'uniq' group provides a unique token, named based on the arg token, to the macro in
* place of the arg. This is equivalent to UNIQ_T() for each arg.
*
* Each arg in the 'var' group provides a temporary variable of the specified type to the macro in place of
* the arg. All args in this group must be types.
*
* The 'varinit' group is similar to the 'var' group, but each arg must be a variable or constant, and each
* temporary variable is initialized to the value of the provided arg. The macro may use these args without
* any concern for side effects.
*
* The 'varconst' group is similar to the 'varinit' group, but the temporary variables are also marked as
* const. The macro should not modify args in this group.
*
* Each arg in the 'direct' group is provided directly to the macro. */
#define VA_MACRO_HELPER(macro, uniq, var, varinit, varconst, direct) \
VA_IF_ELSE(__VMH_STATEMENT_EXPRESSION, \
__VMH_EXPRESSION, \
var varinit varconst)(macro, \
UNIQ, \
VA_GROUP(uniq), \
VA_GROUP(var), \
VA_GROUP(varinit), \
VA_GROUP(varconst), \
VA_GROUP(direct))
/* Same as VA_MACRO_HELPER() but only with 'uniq' group; all variadic args are put in 'direct' group. */
#define VA_MACRO_UNIQ(macro, uniq, ...) \
VA_MACRO_HELPER(macro, \
VA_GROUP(uniq), \
/* var= */, \
/* varinit= */, \
/* varconst= */, \
VA_GROUP(__VA_ARGS__))
/* Same as VA_MACRO_HELPER() but only with 'var' group; all variadic args are put in 'direct' group. */
#define VA_MACRO_VAR(macro, var, ...) \
VA_MACRO_HELPER(macro, \
/* uniq= */, \
VA_GROUP(var), \
/* varinit= */, \
/* varconst= */, \
VA_GROUP(__VA_ARGS__))
/* Same as VA_MACRO_HELPER() but only with 'varinit' group; all variadic args are put in 'direct' group. */
#define VA_MACRO_VARINIT(macro, varinit, ...) \
VA_MACRO_HELPER(macro, \
/* uniq= */, \
/* var= */, \
VA_GROUP(varinit), \
/* varconst= */, \
VA_GROUP(__VA_ARGS__))
/* Same as VA_MACRO_HELPER() but only with 'varconst' group; all variadic args are put in 'direct' group. */
#define VA_MACRO_VARCONST(macro, varconst, ...) \
VA_MACRO_HELPER(macro, \
/* uniq= */, \
/* var= */, \
/* varinit= */, \
VA_GROUP(varconst), \
VA_GROUP(__VA_ARGS__))
/* Macros below are complex, internal-use-only macros and should not be used directly. They are used by the
* macros above. */
/* Integer increment at the preprocessor stage; each macro evaluates to the next integer. Overflow is not
* handled; f wraps to 0. */
#define __VAI0 1
#define __VAI1 2
#define __VAI2 3
#define __VAI3 4
#define __VAI4 5
#define __VAI5 6
#define __VAI6 7
#define __VAI7 8
#define __VAI8 9
#define __VAI9 a
#define __VAIa b
#define __VAIb c
#define __VAIc d
#define __VAId e
#define __VAIe f
#define __VAIf 0
/* Integer increment carryover; all macros evaluate to 0 except f, which evaluates to 1. */
#define __VAC0 0
#define __VAC1 0
#define __VAC2 0
#define __VAC3 0
#define __VAC4 0
#define __VAC5 0
#define __VAC6 0
#define __VAC7 0
#define __VAC8 0
#define __VAC9 0
#define __VACa 0
#define __VACb 0
#define __VACc 0
#define __VACd 0
#define __VACe 0
#define __VACf 1
/* Increment x based on carryover c. Requires x to be single hex digit (0-f) and carryover to be 0-1.
* Evaluates to 0 if x == f and c == 1, otherwise x+1 if c == 1, otherwise x. */
#define ___VAI(x, c) ____VAI(x, c)
#define ____VAI(x, c) ____VAI ## c(x)
#define ____VAI0(x) x
#define ____VAI1(x) __VAI ## x
/* Carryover of x based on carryover c. Requires x to be single hex digit (0-f) and carryover to be
* 0-1. Evaluates to 1 if x is f and c is 1, otherwise 0. */
#define ___VAC(x, c) ____VAC(x, c)
#define ____VAC(x, c) ____VAC ## c(x)
#define ____VAC0(x) 0
#define ____VAC1(x) __VAC ## x
/* Carryover of multiple digits. Each calculates the carryover of its digit, with 1 being the least
* significant digit, and 4 being the most significant digit. */
#define ___VAC1(x1) ___VAC(x1, 1)
#define ___VAC2(x2, x1) ___VAC(x2, ___VAC1(x1))
#define ___VAC3(x3, x2, x1) ___VAC(x3, ___VAC2(x2, x1))
#define ___VAC4(x4, x3, x2, x1) ___VAC(x4, ___VAC3(x3, x2, x1))
/* Increment with carryover across all digits. Each evaluate to their digit incremented if there is carryover
* from previous digits. */
#define ___VAI1(x1) ___VAI(x1, 1)
#define ___VAI2(x2, x1) ___VAI(x2, ___VAC1(x1))
#define ___VAI3(x3, x2, x1) ___VAI(x3, ___VAC2(x2, x1))
#define ___VAI4(x4, x3, x2, x1) ___VAI(x4, ___VAC3(x3, x2, x1))
/* Detect overflow. If all digits are f, this causes preprocessor error, otherwise this evaluates to
* nothing. */
#define ___VAIO(x4, x3, x2, x1) ____VAIO(___VAC4(x4, x3, x2, x1))
#define ____VAIO(c) _____VAIO(c)
#define _____VAIO(c) ______VAIO ## c()
#define ______VAIO0()
#define ______VAIO1() _Pragma("GCC error \"VA increment overflow\"")
/* Increment a 4-digit hex number. Requires pgroup to be a 4-digit hex number pgroup, e.g. (0,1,2,3)
* represents 0x0123. Evaluates to a 4-digit hex number pgroup that has been incremented by 1. On overflow, a
* preprocessor error is generated. */
#define __VAINC4(pgroup) ___VAINC4 pgroup
#define ___VAINC4(x4, x3, x2, x1) \
___VAIO(x4, x3, x2, x1) \
(___VAI4(x4, x3, x2, x1), \
___VAI3(x3, x2, x1), \
___VAI2(x2, x1), \
___VAI1(x1))
/* Convert a 4-digit hex number pgroup to a standard hex number. Requires pgroup to be a 4-digit hex number
* pgroup. Evaluates to a standard hex number for the pgroup, e.g. (a,b,c,d) evalutes to 0xabcd. */
#define __VANUM4(pgroup) ___VANUM4 pgroup
#define ___VANUM4(x4, x3, x2, x1) 0x ## x4 ## x3 ## x2 ## x1
/* Nested repeated evaluations. This is what controls when the 'toomany' VA_WRAP() parameter is evaluated. */
#define __VA_EVAL_0x0002(...) __VA_ARGS__
#define __VA_EVAL_0x0004(...) __VA_EVAL_0x0002(__VA_EVAL_0x0002(__VA_ARGS__))
#define __VA_EVAL_0x0008(...) __VA_EVAL_0x0004(__VA_EVAL_0x0004(__VA_ARGS__))
#define __VA_EVAL_0x0010(...) __VA_EVAL_0x0008(__VA_EVAL_0x0008(__VA_ARGS__))
#define __VA_EVAL_0x0020(...) __VA_EVAL_0x0010(__VA_EVAL_0x0010(__VA_ARGS__))
#define __VA_EVAL_0x0040(...) __VA_EVAL_0x0020(__VA_EVAL_0x0020(__VA_ARGS__))
#define __VA_EVAL_0x0080(...) __VA_EVAL_0x0040(__VA_EVAL_0x0040(__VA_ARGS__))
#define __VA_EVAL_0x0100(...) __VA_EVAL_0x0080(__VA_EVAL_0x0080(__VA_ARGS__))
#define __VA_EVAL_0x0200(...) __VA_EVAL_0x0100(__VA_EVAL_0x0100(__VA_ARGS__))
/* This should match the list of macros above. */
#define __VA_EVAL_STEPS (0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200)
/* Determine which __VA_EVAL_0x* macro to use for the given variadic args. This is a quick evaluation for the
* preprocessor and avoids unnecessary reevaluations for complex macro expansions. Evaluates to the smallest
* (least evaluations) __VA_EVAL_0x* macro token that can handle the number of provided variadic args. */
#define __VA_EVAL_MACRO(...) __VA_EVAL_MACRO_CHECK_EACH(__VA_EVAL_STEPS, __VA_ARGS__)
/* Re-evaluates for each step. If __VA_EVAL_STEPS is increased this may need to be increased. */
#define __VA_EVAL_MACRO_CHECK_EACH(steps, ...) __VA_EVAL_MACRO_EVAL16(__VA_EVAL_MACRO_CHECK(steps, __VA_ARGS__))
#define __VA_EVAL_MACRO_EVAL02(...) __VA_ARGS__
#define __VA_EVAL_MACRO_EVAL04(...) __VA_EVAL_MACRO_EVAL02(__VA_EVAL_MACRO_EVAL02(__VA_ARGS__))
#define __VA_EVAL_MACRO_EVAL08(...) __VA_EVAL_MACRO_EVAL04(__VA_EVAL_MACRO_EVAL04(__VA_ARGS__))
#define __VA_EVAL_MACRO_EVAL16(...) __VA_EVAL_MACRO_EVAL08(__VA_EVAL_MACRO_EVAL08(__VA_ARGS__))
/* Evaluates to the first __VA_EVAL_0x* macro name that can handle all the variadic args. If there are too
* many variadic args for the largest macro to handle, evaluates to nothing. Note this uses the same
* preprocessor recursion "trick" as __VA_WRAP_RECURSE() below. */
#define __VA_EVAL_MACRO_CHECK(steps, ...) \
___VA_EVAL_MACRO_CHECK \
VA_PGROUP(__VA_EVAL_MACRO_MORE(VA_PGROUP_FIRST(steps), __VA_ARGS__))(steps, __VA_ARGS__)
/* 'x' is the evaluation of __VA_EVAL_MACRO_MORE(); if it is empty, this evaluates to __VA_EVAL_MACRO_OK,
* otherwise the tested __VA_EVAL_0x* macro cannot handle all the variadic args, and this evaluates to
* __VA_EVAL_MACRO_NEXT. */
#define ___VA_EVAL_MACRO_CHECK(x) VA_IF_ELSE(__VA_EVAL_MACRO_NEXT, __VA_EVAL_MACRO_OK, x)
/* Move on to testing the next step (i.e. next 0x* value). */
#define __VA_EVAL_MACRO_NEXT(steps, ...) ___VA_EVAL_MACRO_NEXT(VA_PGROUP_REST(steps), __VA_ARGS__)
/* Test the next step value. If there are no more steps, evaluate to nothing. */
#define ___VA_EVAL_MACRO_NEXT(steps, ...) \
VA_MACRO_IF(__VA_EVAL_MACRO_CHECK, VA_PGROUP_NOT_EMPTY(steps), steps, __VA_ARGS__)
/* The first value of 'steps' is acceptable, so evaluate to the corresponding __VA_EVAL_* macro name. */
#define __VA_EVAL_MACRO_OK(steps, ...) ___VA_EVAL_MACRO_OK(VA_PGROUP_FIRST(steps))
#define ___VA_EVAL_MACRO_OK(n) ____VA_EVAL_MACRO_OK(n)
#define ____VA_EVAL_MACRO_OK(n) __VA_EVAL_ ## n
/* Bug in Centos Stream 8 gcc preprocessor doesn't correctly handle __VA_OPT__(); work around it. Once Centos
* Stream 8 is no longer supported, this can be dropped. */
#define __CENTOS_STREAM_8_NONE
#define __CENTOS_STREAM_8_BUG_CHECK() ___CENTOS_STREAM_8_BUG_CHECK(__CENTOS_STREAM_8_NONE)
#define ___CENTOS_STREAM_8_BUG_CHECK(...) __VA_OPT__(1)
#define __VA_EVAL_MACRO_MORE_IF_ONCE(...) __VA_OPT__(1)
#define __VA_EVAL_MACRO_MORE_IF_TWICE(...) __VA_EVAL_MACRO_MORE_IF_ONCE(__VA_ARGS__)
#define __VA_EVAL_MACRO_MORE_IF_MACRO() \
VA_IF_ELSE(__VA_EVAL_MACRO_MORE_IF_TWICE, \
__VA_EVAL_MACRO_MORE_IF_ONCE, \
__CENTOS_STREAM_8_BUG_CHECK())
#define __VA_EVAL_MACRO_MORE_IF() __VA_EVAL_MACRO_MORE_IF_MACRO()
/* Test if the __VA_EVAL_0x* macro for hex number 'n' can handle all the variadic args. Evaluates to 1 if
* there are remaining (unhandled) variadic args after all evaluations, otherwise nothing. */
#define __VA_EVAL_MACRO_MORE(n, ...) \
__VA_EVAL_MACRO_MORE_IF()(__VA_EVAL_MACRO_MORE_N(n)(__VA_OPT__(___VA_EVAL_MACRO_MORE(__VA_ARGS__))))
#define __VA_EVAL_MACRO_MORE_N(n) __VA_EVAL_ ## n
#define ___VA_EVAL_MACRO_MORE(v, ...) __VA_OPT__(___VA_EVAL_MACRO_MORE_NEXT VA_PGROUP()(__VA_ARGS__))
#define ___VA_EVAL_MACRO_MORE_NEXT() ___VA_EVAL_MACRO_MORE
/* Recursive macro evaluation. This is intended for use by VA_WRAP() above. This performs the actions
* described by VA_WRAP() for each variadic arg.
*
* This "trick" inspired by:
* https://www.scs.stanford.edu/~dm/blog/va-opt.html
* https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms
*
* This determines the number of re-evaluations required for the provided number of variadic args, then calls
* the appropriate __VA_EVAL_0x*() macro with ___VA_WRAP_RECURSE(), providing the initial index of 0x0001. */
#define __VA_WRAP_RECURSE(macro, separator, context, zero, toomany, ...) \
VA_IF_ELSE(__VA_WRAP_RECURSE_NONZERO, \
__VA_WRAP_RECURSE_ZERO, \
__VA_ARGS__)(macro, separator, context, zero, toomany, __VA_ARGS__)
#define __VA_WRAP_RECURSE_ZERO(macro, separator, context, zero, toomany, ...) zero(context)
#define __VA_WRAP_RECURSE_NONZERO(macro, separator, context, zero, toomany, ...) \
__VA_WRAP_RECURSE_CHECK_TOOMANY(__VA_EVAL_MACRO(__VA_ARGS__), \
macro, separator, context, toomany, __VA_ARGS__)
#define __VA_WRAP_RECURSE_CHECK_TOOMANY(evalmacro, macro, separator, context, toomany, ...) \
VA_IF_ELSE(__VA_WRAP_RECURSE_EVAL, \
__VA_WRAP_RECURSE_TOOMANY, \
evalmacro)(evalmacro, macro, separator, context, toomany, __VA_ARGS__)
#define __VA_WRAP_RECURSE_TOOMANY(evalmacro, macro, separator, context, toomany, ...) toomany(context)
#define __VA_WRAP_RECURSE_EVAL(evalmacro, macro, separator, context, toomany, ...) \
evalmacro(___VA_WRAP_RECURSE(macro, \
separator, \
context, \
(0,0,0,1), \
__VA_ARGS__))
/* This is the "trick" macro, which evaluates to the current variadic arg 'value' wrapped by 'macro', and
* then (if there are remaining variadic args) followed by 'separator' followed by the "trick"; which is
* ___VA_WRAP_NEXT token and VA_PGROUP(). On the next re-evaluation, this (indirectly) evaluates recursively
* to ___VA_WRAP_RECURSE(). */
#define ___VA_WRAP_RECURSE(macro, separator, context, index, value, ...) \
___VA_WRAP_RECURSE_CALL(macro, \
VA_IF_ELSE(separator, VA_NOOP, __VA_ARGS__), \
VA_GROUP(context, __VANUM4(index), value, __VA_ARGS__)) \
__VA_OPT__(___VA_WRAP_NEXT VA_PGROUP()(macro, separator, context, __VAINC4(index), __VA_ARGS__))
#define ___VA_WRAP_RECURSE_CALL(macro, separator, args) macro(args)separator(args)
#define ___VA_WRAP_NEXT() ___VA_WRAP_RECURSE

View File

@ -2434,6 +2434,8 @@ static int create_interactively(void) {
return 0;
}
any_key_to_proceed();
r = acquire_bus(&bus);
if (r < 0)
return r;

View File

@ -973,7 +973,7 @@ int netdev_load_one(Manager *manager, const char *filename) {
if (r < 0)
return r;
log_netdev_debug(netdev, "loaded \"%s\"", netdev_kind_to_string(netdev->kind));
log_syntax(/* unit = */ NULL, LOG_DEBUG, filename, /* config_line = */ 0, /* error = */ 0, "Successfully loaded.");
r = netdev_request_to_create(netdev);
if (r < 0)

View File

@ -6,6 +6,7 @@
#include <linux/if_arp.h>
#include "alloc-util.h"
#include "device-private.h"
#include "dhcp-client-internal.h"
#include "hostname-setup.h"
#include "hostname-util.h"
@ -1428,27 +1429,33 @@ static int dhcp4_set_request_address(Link *link) {
}
static bool link_needs_dhcp_broadcast(Link *link) {
const char *val;
int r;
assert(link);
assert(link->network);
/* Return the setting in DHCP[4].RequestBroadcast if specified. Otherwise return the device property
* ID_NET_DHCP_BROADCAST setting, which may be set for interfaces requiring that the DHCPOFFER message
* is being broadcast because they can't handle unicast messages while not fully configured.
* If neither is set or a failure occurs, return false, which is the default for this flag.
*/
r = link->network->dhcp_broadcast;
if (r < 0 && link->dev && sd_device_get_property_value(link->dev, "ID_NET_DHCP_BROADCAST", &val) >= 0) {
r = parse_boolean(val);
if (r < 0)
log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to parse ID_NET_DHCP_BROADCAST, ignoring: %m");
else
log_link_debug(link, "DHCPv4 CLIENT: Detected ID_NET_DHCP_BROADCAST='%d'.", r);
* ID_NET_DHCP_BROADCAST setting, which may be set for interfaces requiring that the DHCPOFFER
* message is being broadcast because they can't handle unicast messages while not fully configured.
* If neither is set or a failure occurs, return false, which is the default for this flag. */
r = link->network->dhcp_broadcast;
if (r >= 0)
return r;
if (!link->dev)
return false;
r = device_get_property_bool(link->dev, "ID_NET_DHCP_BROADCAST");
if (r < 0) {
if (r != -ENOENT)
log_link_warning_errno(link, r, "DHCPv4 CLIENT: Failed to get or parse ID_NET_DHCP_BROADCAST, ignoring: %m");
return false;
}
return r == true;
log_link_debug(link, "DHCPv4 CLIENT: Detected ID_NET_DHCP_BROADCAST='%d'.", r);
return r;
}
static bool link_dhcp4_ipv6_only_mode(Link *link) {

View File

@ -1293,9 +1293,9 @@ static int link_get_network(Link *link, Network **ret) {
}
log_link_full(link, warn ? LOG_WARNING : LOG_DEBUG,
"found matching network '%s'%s.",
network->filename,
warn ? ", based on potentially unpredictable interface name" : "");
"Found matching .network file%s: %s",
warn ? ", based on potentially unpredictable interface name" : "",
network->filename);
if (network->unmanaged)
return -ENOENT;
@ -1304,7 +1304,7 @@ static int link_get_network(Link *link, Network **ret) {
return 0;
}
return -ENOENT;
return log_link_debug_errno(link, SYNTHETIC_ERRNO(ENOENT), "No matching .network found.");
}
int link_reconfigure_impl(Link *link, bool force) {

View File

@ -590,6 +590,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
return log_warning_errno(r, "%s: Failed to store configuration into hashmap: %m", filename);
TAKE_PTR(network);
log_syntax(/* unit = */ NULL, LOG_DEBUG, filename, /* config_line = */ 0, /* error = */ 0, "Successfully loaded.");
return 0;
}

View File

@ -404,6 +404,10 @@ typedef struct Partition {
PartitionEncryptedVolume *encrypted_volume;
char *supplement_for_name;
struct Partition *supplement_for, *supplement_target_for;
struct Partition *suppressing;
struct Partition *siblings[_VERITY_MODE_MAX];
LIST_FIELDS(struct Partition, partitions);
@ -411,6 +415,7 @@ typedef struct Partition {
#define PARTITION_IS_FOREIGN(p) (!(p)->definition_path)
#define PARTITION_EXISTS(p) (!!(p)->current_partition)
#define PARTITION_SUPPRESSED(p) ((p)->supplement_for && (p)->supplement_for->suppressing == (p))
struct FreeArea {
Partition *after;
@ -520,6 +525,28 @@ static Partition *partition_new(void) {
return p;
}
static void partition_unlink_supplement(Partition *p) {
assert(p);
assert(!p->supplement_for || !p->supplement_target_for); /* Can't be both */
if (p->supplement_target_for) {
assert(p->supplement_target_for->supplement_for == p);
p->supplement_target_for->supplement_for = NULL;
}
if (p->supplement_for) {
assert(p->supplement_for->supplement_target_for == p);
assert(!p->supplement_for->suppressing || p->supplement_for->suppressing == p);
p->supplement_for->supplement_target_for = p->supplement_for->suppressing = NULL;
}
p->supplement_for_name = mfree(p->supplement_for_name);
p->supplement_target_for = p->supplement_for = p->suppressing = NULL;
}
static Partition* partition_free(Partition *p) {
if (!p)
return NULL;
@ -563,6 +590,8 @@ static Partition* partition_free(Partition *p) {
partition_encrypted_volume_free(p->encrypted_volume);
partition_unlink_supplement(p);
return mfree(p);
}
@ -608,6 +637,8 @@ static void partition_foreignize(Partition *p) {
p->n_mountpoints = 0;
p->encrypted_volume = partition_encrypted_volume_free(p->encrypted_volume);
partition_unlink_supplement(p);
}
static bool partition_type_exclude(const GptPartitionType *type) {
@ -740,6 +771,10 @@ static void partition_drop_or_foreignize(Partition *p) {
p->dropped = true;
p->allocated_to_area = NULL;
/* If a supplement partition is dropped, we don't want to merge in its settings. */
if (PARTITION_SUPPRESSED(p))
p->supplement_for->suppressing = NULL;
}
}
@ -775,7 +810,7 @@ static bool context_drop_or_foreignize_one_priority(Context *context) {
}
static uint64_t partition_min_size(const Context *context, const Partition *p) {
uint64_t sz;
uint64_t sz, override_min;
assert(context);
assert(p);
@ -817,11 +852,13 @@ static uint64_t partition_min_size(const Context *context, const Partition *p) {
sz = d;
}
return MAX(round_up_size(p->size_min != UINT64_MAX ? p->size_min : DEFAULT_MIN_SIZE, context->grain_size), sz);
override_min = p->suppressing ? MAX(p->size_min, p->suppressing->size_min) : p->size_min;
return MAX(round_up_size(override_min != UINT64_MAX ? override_min : DEFAULT_MIN_SIZE, context->grain_size), sz);
}
static uint64_t partition_max_size(const Context *context, const Partition *p) {
uint64_t sm;
uint64_t sm, override_max;
/* Calculate how large the partition may become at max. This is generally the configured maximum
* size, except when it already exists and is larger than that. In that case it's the existing size,
@ -839,10 +876,11 @@ static uint64_t partition_max_size(const Context *context, const Partition *p) {
if (p->verity == VERITY_SIG)
return VERITY_SIG_SIZE;
if (p->size_max == UINT64_MAX)
override_max = p->suppressing ? MIN(p->size_max, p->suppressing->size_max) : p->size_max;
if (override_max == UINT64_MAX)
return UINT64_MAX;
sm = round_down_size(p->size_max, context->grain_size);
sm = round_down_size(override_max, context->grain_size);
if (p->current_size != UINT64_MAX)
sm = MAX(p->current_size, sm);
@ -851,13 +889,17 @@ static uint64_t partition_max_size(const Context *context, const Partition *p) {
}
static uint64_t partition_min_padding(const Partition *p) {
uint64_t override_min;
assert(p);
return p->padding_min != UINT64_MAX ? p->padding_min : 0;
override_min = p->suppressing ? MAX(p->padding_min, p->suppressing->padding_min) : p->padding_min;
return override_min != UINT64_MAX ? override_min : 0;
}
static uint64_t partition_max_padding(const Partition *p) {
assert(p);
return p->padding_max;
return p->suppressing ? MIN(p->padding_max, p->suppressing->padding_max) : p->padding_max;
}
static uint64_t partition_min_size_with_padding(Context *context, const Partition *p) {
@ -977,14 +1019,22 @@ static bool context_allocate_partitions(Context *context, uint64_t *ret_largest_
uint64_t required;
FreeArea *a = NULL;
/* Skip partitions we already dropped or that already exist */
if (p->dropped || PARTITION_EXISTS(p))
if (p->dropped || PARTITION_IS_FOREIGN(p) || PARTITION_SUPPRESSED(p))
continue;
/* How much do we need to fit? */
required = partition_min_size_with_padding(context, p);
assert(required % context->grain_size == 0);
/* For existing partitions, we should verify that they'll actually fit */
if (PARTITION_EXISTS(p)) {
if (p->current_size + p->current_padding < required)
return false; /* 😢 We won't be able to grow to the required min size! */
continue;
}
/* For new partitions, see if there's a free area big enough */
for (size_t i = 0; i < context->n_free_areas; i++) {
a = context->free_areas[i];
@ -1007,6 +1057,57 @@ static bool context_allocate_partitions(Context *context, uint64_t *ret_largest_
return true;
}
static bool context_unmerge_and_allocate_partitions(Context *context) {
assert(context);
/* This should only be called after plain context_allocate_partitions fails. This algorithm will
* try, in the order that minimizes the number of created supplement partitions, all combinations of
* un-suppressing supplement partitions until it finds one that works. */
/* First, let's try to un-suppress just one supplement partition and see if that gets us anywhere */
LIST_FOREACH(partitions, p, context->partitions) {
Partition *unsuppressed;
if (!p->suppressing)
continue;
unsuppressed = TAKE_PTR(p->suppressing);
if (context_allocate_partitions(context, NULL))
return true;
p->suppressing = unsuppressed;
}
/* Looks like not. So we have to un-suppress at least two partitions. We can do this recursively */
LIST_FOREACH(partitions, p, context->partitions) {
Partition *unsuppressed;
if (!p->suppressing)
continue;
unsuppressed = TAKE_PTR(p->suppressing);
if (context_unmerge_and_allocate_partitions(context))
return true;
p->suppressing = unsuppressed;
}
/* No combination of un-suppressed supplements made it possible to fit the partitions */
return false;
}
static uint32_t partition_weight(const Partition *p) {
assert(p);
return p->suppressing ? p->suppressing->weight : p->weight;
}
static uint32_t partition_padding_weight(const Partition *p) {
assert(p);
return p->suppressing ? p->suppressing->padding_weight : p->padding_weight;
}
static int context_sum_weights(Context *context, FreeArea *a, uint64_t *ret) {
uint64_t weight_sum = 0;
@ -1020,13 +1121,11 @@ static int context_sum_weights(Context *context, FreeArea *a, uint64_t *ret) {
if (p->padding_area != a && p->allocated_to_area != a)
continue;
if (p->weight > UINT64_MAX - weight_sum)
if (!INC_SAFE(&weight_sum, partition_weight(p)))
goto overflow_sum;
weight_sum += p->weight;
if (p->padding_weight > UINT64_MAX - weight_sum)
if (!INC_SAFE(&weight_sum, partition_padding_weight(p)))
goto overflow_sum;
weight_sum += p->padding_weight;
}
*ret = weight_sum;
@ -1091,7 +1190,6 @@ static bool context_grow_partitions_phase(
* get any additional room from the left-overs. Similar, if two partitions have the same weight they
* should get the same space if possible, even if one has a smaller minimum size than the other. */
LIST_FOREACH(partitions, p, context->partitions) {
/* Look only at partitions associated with this free area, i.e. immediately
* preceding it, or allocated into it */
if (p->allocated_to_area != a && p->padding_area != a)
@ -1099,11 +1197,14 @@ static bool context_grow_partitions_phase(
if (p->new_size == UINT64_MAX) {
uint64_t share, rsz, xsz;
uint32_t weight;
bool charge = false;
weight = partition_weight(p);
/* Calculate how much this space this partition needs if everyone would get
* the weight based share */
share = scale_by_weight(*span, p->weight, *weight_sum);
share = scale_by_weight(*span, weight, *weight_sum);
rsz = partition_min_size(context, p);
xsz = partition_max_size(context, p);
@ -1143,15 +1244,18 @@ static bool context_grow_partitions_phase(
if (charge) {
*span = charge_size(context, *span, p->new_size);
*weight_sum = charge_weight(*weight_sum, p->weight);
*weight_sum = charge_weight(*weight_sum, weight);
}
}
if (p->new_padding == UINT64_MAX) {
uint64_t share, rsz, xsz;
uint32_t padding_weight;
bool charge = false;
share = scale_by_weight(*span, p->padding_weight, *weight_sum);
padding_weight = partition_padding_weight(p);
share = scale_by_weight(*span, padding_weight, *weight_sum);
rsz = partition_min_padding(p);
xsz = partition_max_padding(p);
@ -1170,7 +1274,7 @@ static bool context_grow_partitions_phase(
if (charge) {
*span = charge_size(context, *span, p->new_padding);
*weight_sum = charge_weight(*weight_sum, p->padding_weight);
*weight_sum = charge_weight(*weight_sum, padding_weight);
}
}
}
@ -2155,7 +2259,9 @@ static int partition_finalize_fstype(Partition *p, const char *path) {
static bool partition_needs_populate(const Partition *p) {
assert(p);
return !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories) || !strv_isempty(p->make_symlinks);
assert(!p->supplement_for || !p->suppressing); /* Avoid infinite recursion */
return !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories) || !strv_isempty(p->make_symlinks) ||
(p->suppressing && partition_needs_populate(p->suppressing));
}
static int partition_read_definition(Partition *p, const char *path, const char *const *conf_file_dirs) {
@ -2196,6 +2302,7 @@ static int partition_read_definition(Partition *p, const char *path, const char
{ "Partition", "EncryptedVolume", config_parse_encrypted_volume, 0, p },
{ "Partition", "Compression", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression },
{ "Partition", "CompressionLevel", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression_level },
{ "Partition", "SupplementFor", config_parse_string, 0, &p->supplement_for_name },
{}
};
_cleanup_free_ char *filename = NULL;
@ -2320,6 +2427,18 @@ static int partition_read_definition(Partition *p, const char *path, const char
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"DefaultSubvolume= must be one of the paths in Subvolumes=.");
if (p->supplement_for_name) {
if (!filename_part_is_valid(p->supplement_for_name))
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"SupplementFor= is an invalid filename: %s",
p->supplement_for_name);
if (p->copy_blocks_path || p->copy_blocks_auto || p->encrypt != ENCRYPT_OFF ||
p->verity != VERITY_OFF)
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"SupplementFor= cannot be combined with CopyBlocks=/Encrypt=/Verity=");
}
/* Verity partitions are read only, let's imply the RO flag hence, unless explicitly configured otherwise. */
if ((IN_SET(p->type.designator,
PARTITION_ROOT_VERITY,
@ -2626,6 +2745,58 @@ static int context_copy_from(Context *context) {
return 0;
}
static bool check_cross_def_ranges_valid(uint64_t a_min, uint64_t a_max, uint64_t b_min, uint64_t b_max) {
if (a_min == UINT64_MAX && b_min == UINT64_MAX)
return true;
if (a_max == UINT64_MAX && b_max == UINT64_MAX)
return true;
return MAX(a_min != UINT64_MAX ? a_min : 0, b_min != UINT64_MAX ? b_min : 0) <= MIN(a_max, b_max);
}
static int supplement_find_target(const Context *context, const Partition *supplement, Partition **ret) {
int r;
assert(context);
assert(supplement);
assert(ret);
LIST_FOREACH(partitions, p, context->partitions) {
_cleanup_free_ char *filename = NULL;
if (p == supplement)
continue;
r = path_extract_filename(p->definition_path, &filename);
if (r < 0)
return log_error_errno(r,
"Failed to extract filename from path '%s': %m",
p->definition_path);
*ASSERT_PTR(endswith(filename, ".conf")) = 0; /* Remove the file extension */
if (!streq(supplement->supplement_for_name, filename))
continue;
if (p->supplement_for_name)
return log_syntax(NULL, LOG_ERR, supplement->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
"SupplementFor= target is itself configured as a supplement.");
if (p->suppressing)
return log_syntax(NULL, LOG_ERR, supplement->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
"SupplementFor= target already has a supplement defined: %s",
p->suppressing->definition_path);
*ret = p;
return 0;
}
return log_syntax(NULL, LOG_ERR, supplement->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
"Couldn't find target partition for SupplementFor=%s",
supplement->supplement_for_name);
}
static int context_read_definitions(Context *context) {
_cleanup_strv_free_ char **files = NULL;
Partition *last = LIST_FIND_TAIL(partitions, context->partitions);
@ -2717,7 +2888,33 @@ static int context_read_definitions(Context *context) {
if (dp->minimize == MINIMIZE_OFF && !(dp->copy_blocks_path || dp->copy_blocks_auto))
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
"Minimize= set for verity hash partition but data partition does not set CopyBlocks= or Minimize=.");
}
LIST_FOREACH(partitions, p, context->partitions) {
Partition *tgt = NULL;
if (!p->supplement_for_name)
continue;
r = supplement_find_target(context, p, &tgt);
if (r < 0)
return r;
if (tgt->copy_blocks_path || tgt->copy_blocks_auto || tgt->encrypt != ENCRYPT_OFF ||
tgt->verity != VERITY_OFF)
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
"SupplementFor= target uses CopyBlocks=/Encrypt=/Verity=");
if (!check_cross_def_ranges_valid(p->size_min, p->size_max, tgt->size_min, tgt->size_max))
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
"SizeMinBytes= larger than SizeMaxBytes= when merged with SupplementFor= target.");
if (!check_cross_def_ranges_valid(p->padding_min, p->padding_max, tgt->padding_min, tgt->padding_max))
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
"PaddingMinBytes= larger than PaddingMaxBytes= when merged with SupplementFor= target.");
p->supplement_for = tgt;
tgt->suppressing = tgt->supplement_target_for = p;
}
return 0;
@ -3101,6 +3298,10 @@ static int context_load_partition_table(Context *context) {
}
}
LIST_FOREACH(partitions, p, context->partitions)
if (PARTITION_SUPPRESSED(p) && PARTITION_EXISTS(p))
p->supplement_for->suppressing = NULL;
add_initial_free_area:
nsectors = fdisk_get_nsectors(c);
assert(nsectors <= UINT64_MAX/secsz);
@ -3192,6 +3393,11 @@ static void context_unload_partition_table(Context *context) {
p->current_uuid = SD_ID128_NULL;
p->current_label = mfree(p->current_label);
/* A supplement partition is only ever un-suppressed if the existing partition table prevented
* us from suppressing it. So when unloading the partition table, we must re-suppress. */
if (p->supplement_for)
p->supplement_for->suppressing = p;
}
context->start = UINT64_MAX;
@ -4969,6 +5175,31 @@ static int add_exclude_path(const char *path, Hashmap **denylist, DenyType type)
return 0;
}
static int shallow_join_strv(char ***ret, char **a, char **b) {
_cleanup_free_ char **joined = NULL;
char **iter;
assert(ret);
joined = new(char*, strv_length(a) + strv_length(b) + 1);
if (!joined)
return log_oom();
iter = joined;
STRV_FOREACH(i, a)
*(iter++) = *i;
STRV_FOREACH(i, b)
if (!strv_contains(joined, *i))
*(iter++) = *i;
*iter = NULL;
*ret = TAKE_PTR(joined);
return 0;
}
static int make_copy_files_denylist(
Context *context,
const Partition *p,
@ -4977,6 +5208,7 @@ static int make_copy_files_denylist(
Hashmap **ret) {
_cleanup_hashmap_free_ Hashmap *denylist = NULL;
_cleanup_free_ char **override_exclude_src = NULL, **override_exclude_tgt = NULL;
int r;
assert(context);
@ -4996,13 +5228,26 @@ static int make_copy_files_denylist(
/* Add the user configured excludes. */
STRV_FOREACH(e, p->exclude_files_source) {
if (p->suppressing) {
r = shallow_join_strv(&override_exclude_src,
p->exclude_files_source,
p->suppressing->exclude_files_source);
if (r < 0)
return r;
r = shallow_join_strv(&override_exclude_tgt,
p->exclude_files_target,
p->suppressing->exclude_files_target);
if (r < 0)
return r;
}
STRV_FOREACH(e, override_exclude_src ?: p->exclude_files_source) {
r = add_exclude_path(*e, &denylist, endswith(*e, "/") ? DENY_CONTENTS : DENY_INODE);
if (r < 0)
return r;
}
STRV_FOREACH(e, p->exclude_files_target) {
STRV_FOREACH(e, override_exclude_tgt ?: p->exclude_files_target) {
_cleanup_free_ char *path = NULL;
const char *s = path_startswith(*e, target);
@ -5096,6 +5341,7 @@ static int add_subvolume_path(const char *path, Set **subvolumes) {
static int make_subvolumes_strv(const Partition *p, char ***ret) {
_cleanup_strv_free_ char **subvolumes = NULL;
Subvolume *subvolume;
int r;
assert(p);
assert(ret);
@ -5104,6 +5350,18 @@ static int make_subvolumes_strv(const Partition *p, char ***ret) {
if (strv_extend(&subvolumes, subvolume->path) < 0)
return log_oom();
if (p->suppressing) {
_cleanup_strv_free_ char **suppressing = NULL;
r = make_subvolumes_strv(p->suppressing, &suppressing);
if (r < 0)
return r;
r = strv_extend_strv(&subvolumes, suppressing, /* filter_duplicates= */ true);
if (r < 0)
return log_oom();
}
*ret = TAKE_PTR(subvolumes);
return 0;
}
@ -5114,18 +5372,22 @@ static int make_subvolumes_set(
const char *target,
Set **ret) {
_cleanup_strv_free_ char **paths = NULL;
_cleanup_set_free_ Set *subvolumes = NULL;
Subvolume *subvolume;
int r;
assert(p);
assert(target);
assert(ret);
ORDERED_HASHMAP_FOREACH(subvolume, p->subvolumes) {
r = make_subvolumes_strv(p, &paths);
if (r < 0)
return r;
STRV_FOREACH(subvolume, paths) {
_cleanup_free_ char *path = NULL;
const char *s = path_startswith(subvolume->path, target);
const char *s = path_startswith(*subvolume, target);
if (!s)
continue;
@ -5168,6 +5430,7 @@ static usec_t epoch_or_infinity(void) {
static int do_copy_files(Context *context, Partition *p, const char *root) {
_cleanup_strv_free_ char **subvolumes = NULL;
_cleanup_free_ char **override_copy_files = NULL;
int r;
assert(p);
@ -5177,11 +5440,17 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
if (r < 0)
return r;
if (p->suppressing) {
r = shallow_join_strv(&override_copy_files, p->copy_files, p->suppressing->copy_files);
if (r < 0)
return r;
}
/* copy_tree_at() automatically copies the permissions of source directories to target directories if
* it created them. However, the root directory is created by us, so we have to manually take care
* that it is initialized. We use the first source directory targeting "/" as the metadata source for
* the root directory. */
STRV_FOREACH_PAIR(source, target, p->copy_files) {
STRV_FOREACH_PAIR(source, target, override_copy_files ?: p->copy_files) {
_cleanup_close_ int rfd = -EBADF, sfd = -EBADF;
if (!path_equal(*target, "/"))
@ -5202,7 +5471,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
break;
}
STRV_FOREACH_PAIR(source, target, p->copy_files) {
STRV_FOREACH_PAIR(source, target, override_copy_files ?: p->copy_files) {
_cleanup_hashmap_free_ Hashmap *denylist = NULL;
_cleanup_set_free_ Set *subvolumes_by_source_inode = NULL;
_cleanup_close_ int sfd = -EBADF, pfd = -EBADF, tfd = -EBADF;
@ -5320,6 +5589,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
static int do_make_directories(Partition *p, const char *root) {
_cleanup_strv_free_ char **subvolumes = NULL;
_cleanup_free_ char **override_dirs = NULL;
int r;
assert(p);
@ -5329,7 +5599,13 @@ static int do_make_directories(Partition *p, const char *root) {
if (r < 0)
return r;
STRV_FOREACH(d, p->make_directories) {
if (p->suppressing) {
r = shallow_join_strv(&override_dirs, p->make_directories, p->suppressing->make_directories);
if (r < 0)
return r;
}
STRV_FOREACH(d, override_dirs ?: p->make_directories) {
r = mkdir_p_root_full(root, *d, UID_INVALID, GID_INVALID, 0755, epoch_or_infinity(), subvolumes);
if (r < 0)
return log_error_errno(r, "Failed to create directory '%s' in file system: %m", *d);
@ -5377,6 +5653,12 @@ static int make_subvolumes_read_only(Partition *p, const char *root) {
return log_error_errno(r, "Failed to make subvolume '%s' read-only: %m", subvolume->path);
}
if (p->suppressing) {
r = make_subvolumes_read_only(p->suppressing, root);
if (r < 0)
return r;
}
return 0;
}
@ -5496,6 +5778,38 @@ static int partition_populate_filesystem(Context *context, Partition *p, const c
return 0;
}
static int append_btrfs_subvols(char ***l, OrderedHashmap *subvolumes, const char *default_subvolume) {
Subvolume *subvolume;
int r;
assert(l);
ORDERED_HASHMAP_FOREACH(subvolume, subvolumes) {
_cleanup_free_ char *s = NULL, *f = NULL;
s = strdup(subvolume->path);
if (!s)
return log_oom();
f = subvolume_flags_to_string(subvolume->flags);
if (!f)
return log_oom();
if (streq_ptr(subvolume->path, default_subvolume) &&
!strextend_with_separator(&f, ",", "default"))
return log_oom();
if (!isempty(f) && !strextend_with_separator(&s, ":", f))
return log_oom();
r = strv_extend_many(l, "--subvol", s);
if (r < 0)
return log_oom();
}
return 0;
}
static int finalize_extra_mkfs_options(const Partition *p, const char *root, char ***ret) {
_cleanup_strv_free_ char **sv = NULL;
int r;
@ -5510,28 +5824,14 @@ static int finalize_extra_mkfs_options(const Partition *p, const char *root, cha
p->format);
if (partition_needs_populate(p) && root && streq(p->format, "btrfs")) {
Subvolume *subvolume;
ORDERED_HASHMAP_FOREACH(subvolume, p->subvolumes) {
_cleanup_free_ char *s = NULL, *f = NULL;
s = strdup(subvolume->path);
if (!s)
return log_oom();
f = subvolume_flags_to_string(subvolume->flags);
if (!f)
return log_oom();
if (streq_ptr(subvolume->path, p->default_subvolume) && !strextend_with_separator(&f, ",", "default"))
return log_oom();
if (!isempty(f) && !strextend_with_separator(&s, ":", f))
return log_oom();
r = strv_extend_many(&sv, "--subvol", s);
r = append_btrfs_subvols(&sv, p->subvolumes, p->default_subvolume);
if (r < 0)
return log_oom();
return r;
if (p->suppressing) {
r = append_btrfs_subvols(&sv, p->suppressing->subvolumes, NULL);
if (r < 0)
return r;
}
}
@ -8524,7 +8824,7 @@ static int determine_auto_size(Context *c) {
LIST_FOREACH(partitions, p, c->partitions) {
uint64_t m;
if (p->dropped)
if (p->dropped || PARTITION_SUPPRESSED(p))
continue;
m = partition_min_size_with_padding(c, p);
@ -8756,13 +9056,36 @@ static int run(int argc, char *argv[]) {
if (context_allocate_partitions(context, &largest_free_area))
break; /* Success! */
if (!context_drop_or_foreignize_one_priority(context)) {
if (context_unmerge_and_allocate_partitions(context))
break; /* We had to un-suppress a supplement or few, but still success! */
if (context_drop_or_foreignize_one_priority(context))
continue; /* Still no luck. Let's drop a priority and try again. */
/* No more priorities left to drop. This configuration just doesn't fit on this disk... */
r = log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
"Can't fit requested partitions into available free space (%s), refusing.",
FORMAT_BYTES(largest_free_area));
determine_auto_size(context);
return r;
}
LIST_FOREACH(partitions, p, context->partitions) {
if (!p->supplement_for)
continue;
if (PARTITION_SUPPRESSED(p)) {
assert(!p->allocated_to_area);
p->dropped = true;
log_debug("Partition %s can be merged into %s, suppressing supplement.",
p->definition_path, p->supplement_for->definition_path);
} else if (PARTITION_EXISTS(p))
log_info("Partition %s already exists on disk, using supplement verbatim.",
p->definition_path);
else
log_info("Couldn't allocate partitions with %s merged into %s, using supplement verbatim.",
p->definition_path, p->supplement_for->definition_path);
}
/* Now assign free space according to the weight logic */

View File

@ -465,10 +465,6 @@ int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const s
assert(path);
assert(st);
r = hashmap_ensure_allocated(stats_by_path, &path_hash_ops_free_free);
if (r < 0)
return r;
st_copy = newdup(struct stat, st, 1);
if (!st_copy)
return -ENOMEM;
@ -477,7 +473,7 @@ int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const s
if (!path_copy)
return -ENOMEM;
r = hashmap_put(*stats_by_path, path_copy, st_copy);
r = hashmap_ensure_put(stats_by_path, &path_hash_ops_free_free, path_copy, st_copy);
if (r < 0)
return r;
@ -502,12 +498,12 @@ static int config_parse_many_files(
_cleanup_ordered_hashmap_free_ OrderedHashmap *dropins = NULL;
_cleanup_set_free_ Set *inodes = NULL;
struct stat st;
int r;
int r, level = FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_WARNING : LOG_DEBUG;
if (ret_stats_by_path) {
stats_by_path = hashmap_new(&path_hash_ops_free_free);
if (!stats_by_path)
return -ENOMEM;
return log_oom_full(level);
}
STRV_FOREACH(fn, files) {
@ -518,14 +514,14 @@ static int config_parse_many_files(
if (r == -ENOENT)
continue;
if (r < 0)
return r;
return log_full_errno(level, r, "Failed to open %s: %m", *fn);
int fd = fileno(f);
r = ordered_hashmap_ensure_put(&dropins, &config_file_hash_ops_fclose, *fn, f);
if (r < 0) {
assert(r != -EEXIST);
return r;
assert(r == -ENOMEM);
return log_oom_full(level);
}
assert(r > 0);
TAKE_PTR(f);
@ -535,14 +531,14 @@ static int config_parse_many_files(
_cleanup_free_ struct stat *st_dropin = new(struct stat, 1);
if (!st_dropin)
return -ENOMEM;
return log_oom_full(level);
if (fstat(fd, st_dropin) < 0)
return -errno;
return log_full_errno(level, errno, "Failed to stat %s: %m", *fn);
r = set_ensure_consume(&inodes, &inode_hash_ops, TAKE_PTR(st_dropin));
if (r < 0)
return r;
return log_oom_full(level);
}
/* First read the first found main config file. */
@ -553,11 +549,11 @@ static int config_parse_many_files(
if (r == -ENOENT)
continue;
if (r < 0)
return r;
return log_full_errno(level, r, "Failed to open %s: %m", *fn);
if (inodes) {
if (fstat(fileno(f), &st) < 0)
return -errno;
return log_full_errno(level, errno, "Failed to stat %s: %m", *fn);
if (set_contains(inodes, &st)) {
log_debug("%s: symlink to/symlinked as drop-in, will be read later.", *fn);
@ -567,13 +563,13 @@ static int config_parse_many_files(
r = config_parse(/* unit= */ NULL, *fn, f, sections, lookup, table, flags, userdata, &st);
if (r < 0)
return r;
return r; /* config_parse() logs internally. */
assert(r > 0);
if (ret_stats_by_path) {
r = hashmap_put_stats_by_path(&stats_by_path, *fn, &st);
if (r < 0)
return r;
return log_full_errno(level, r, "Failed to save stats of %s: %m", *fn);
}
break;
@ -586,13 +582,13 @@ static int config_parse_many_files(
ORDERED_HASHMAP_FOREACH_KEY(f_dropin, path_dropin, dropins) {
r = config_parse(/* unit= */ NULL, path_dropin, f_dropin, sections, lookup, table, flags, userdata, &st);
if (r < 0)
return r;
return r; /* config_parse() logs internally. */
assert(r > 0);
if (ret_stats_by_path) {
r = hashmap_put_stats_by_path(&stats_by_path, path_dropin, &st);
if (r < 0)
return r;
return log_full_errno(level, r, "Failed to save stats of %s: %m", path_dropin);
}
}
@ -625,11 +621,12 @@ int config_parse_many(
r = conf_files_list_dropins(&files, dropin_dirname, root, conf_file_dirs);
if (r < 0)
return r;
return log_full_errno(FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_WARNING : LOG_DEBUG, r,
"Failed to list up drop-in configs in %s: %m", dropin_dirname);
r = config_parse_many_files(root, conf_files, files, sections, lookup, table, flags, userdata, ret_stats_by_path);
if (r < 0)
return r;
return r; /* config_parse_many_files() logs internally. */
if (ret_dropin_files)
*ret_dropin_files = TAKE_PTR(files);
@ -650,22 +647,16 @@ int config_parse_standard_file_with_dropins_full(
const char* const *conf_paths = (const char* const*) CONF_PATHS_STRV("");
_cleanup_strv_free_ char **configs = NULL;
int r;
int r, level = FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_WARNING : LOG_DEBUG;
/* Build the list of main config files */
r = strv_extend_strv_biconcat(&configs, root, conf_paths, main_file);
if (r < 0) {
if (flags & CONFIG_PARSE_WARN)
log_oom();
return r;
}
if (r < 0)
return log_oom_full(level);
_cleanup_free_ char *dropin_dirname = strjoin(main_file, ".d");
if (!dropin_dirname) {
if (flags & CONFIG_PARSE_WARN)
log_oom();
return -ENOMEM;
}
if (!dropin_dirname)
return log_oom_full(level);
return config_parse_many(
(const char* const*) configs,

View File

@ -184,6 +184,7 @@ simple_tests += files(
'test-unit-file.c',
'test-user-util.c',
'test-utf8.c',
'test-variadic.c',
'test-verbs.c',
'test-vpick.c',
'test-web-util.c',

726
src/test/test-variadic.c Normal file
View File

@ -0,0 +1,726 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <stddef.h>
#include "log.h"
#include "string-util.h"
#include "tests.h"
#include "variadic-fundamental.h"
#define _MACRO_LOG(...) ({ log_info("%s", #__VA_ARGS__); 0; })
#define MACRO_LOG(...) _MACRO_LOG(__VA_ARGS__)
#define NONE
#define MACRO_NONE()
#define MACRO_IGNORE(...)
#define MACRO1(x, ...) (x)
#define MACRO2(x1, x2, ...) (x2)
#define MACRO_SUM12(x1, x2, ...) (x1 + x2)
#define MACRO_3ARG_SUM(x1, x2, x3) x1 + x2 + x3
#define MACRO_4ARG_SUM(x1, x2, x3, x4) x1 + x2 + x3 + x4
#define MACRO_VARG_1(x1, ...) x1
#define MACRO_VARG_2(x1, ...) MACRO_VARG_1(__VA_ARGS__)
#define MACRO_VARG_3(x1, ...) MACRO_VARG_2(__VA_ARGS__)
#define MACRO_VARG_4(x1, ...) MACRO_VARG_3(__VA_ARGS__)
#define MACRO_GROUP_VARG_1(x) MACRO_VARG_1(x)
#define MACRO_GROUP_VARG_2(x) MACRO_VARG_2(x)
#define MACRO_GROUP_VARG_3(x) MACRO_VARG_3(x)
#define MACRO_GROUP_VARG_4(x) MACRO_VARG_4(x)
#define MACRO_GROUP_3ARG_SUM(x) MACRO_3ARG_SUM(x)
#define MACRO_GROUP_4ARG_SUM(x) MACRO_4ARG_SUM(x)
#define MACRO_2GROUP_4ARG_3ARG_SUM(g1, g2) MACRO_4ARG_SUM(g1) + MACRO_3ARG_SUM(g2)
#define MACRO_2GROUP_VARG_3ARG_G2A2(g1, g2) MACRO_VARG_2(g2)
#define MACRO_2GROUP_4ARG_VARG_SUM_G1A4_G2A3(g1, g2) MACRO_VARG_4(g1) + MACRO_VARG_3(g2)
TEST(va_group) {
assert_se(MACRO_GROUP_VARG_4(VA_GROUP(1,2,3,4)) == 4);
assert_se(MACRO_GROUP_VARG_1(VA_GROUP(5,10,20)) == 5);
assert_se(MACRO_GROUP_3ARG_SUM(VA_GROUP(1, 1000, -2)) == 999);
assert_se(MACRO_GROUP_4ARG_SUM(VA_GROUP(1, 1, 1, 2)) == 5);
assert_se(MACRO_2GROUP_4ARG_3ARG_SUM(VA_GROUP(5,6,7,8), VA_GROUP(1,1,1)) == 29);
assert_se(MACRO_2GROUP_VARG_3ARG_G2A2(VA_GROUP(1,2,3,4,5,6,7,8,9), VA_GROUP(3,2,1)) == 2);
assert_se(MACRO_2GROUP_4ARG_VARG_SUM_G1A4_G2A3(VA_GROUP(4,3,2,1), VA_GROUP(9,8,7,6,5,4)) == 8);
}
#define V1() 1
#define V2() 2
#define VI6E7(x) VA_IF_ELSE(6, 7, x)
#define VI8E9(x) VA_IF_ELSE(8, 9, x)
TEST(va_if_else) {
assert_se(VA_IF_ELSE(1,2) == 2);
assert_se(VA_IF_ELSE(1,2,) == 2);
assert_se(VA_IF_ELSE(1,2, ) == 2);
assert_se(VA_IF_ELSE(1,2,NONE) == 2);
assert_se(VA_IF_ELSE(1,2, NONE) == 2);
assert_se(VA_IF_ELSE(1,2,,) == 1);
assert_se(VA_IF_ELSE(1, 2, ) == 2);
assert_se(VA_IF_ELSE(1, 2,NONE ) == 2);
assert_se(VA_IF_ELSE(1, 2, 1) == 1);
assert_se(VA_IF_ELSE(1, 2, "no") == 1);
assert_se(VA_IF_ELSE(1, 2, VA_IF(1, )) == 2);
assert_se(VA_IF_ELSE(1, 2, VA_IF(1, 1) ) == 1);
assert_se(VA_IF_ELSE(1, 2, VA_IF_NOT(1, )) == 1);
assert_se(VA_IF_ELSE(1, 2, VA_IF_NOT(1, 2)) == 2);
assert_se(VA_IF_ELSE(1, 2, VA_NOT()) == 1);
assert_se(VA_IF_ELSE(1, 2, VA_NOT(1)) == 2);
assert_se(VA_IF_ELSE(1, 2, VA_IF_ELSE(100, 200, )) == 1);
assert_se(VA_IF_ELSE(1, 2, VA_IF_ELSE(100, 200, 1)) == 1);
assert_se(VA_IF_ELSE(1, 2, VA_IF_ELSE(100, , )) == 2);
assert_se(VA_IF_ELSE(1, 2, VA_IF_ELSE(, 4 , )) == 1);
assert_se(VA_IF_ELSE(V1, V2, 1)() == 1);
assert_se(VA_IF_ELSE(V1, V2, )() == 2);
assert_se(VA_IF_ELSE(VI6E7, VI8E9, )(1) == 8);
assert_se(VA_IF_ELSE(VI6E7, VI8E9, 0)(1) == 6);
assert_se(VA_IF_ELSE(VI6E7, VI8E9, )() == 9);
assert_se(VA_IF_ELSE(VI6E7, VI8E9, 55)() == 7);
assert_se(VA_IF_ELSE(VA_IF_ELSE(3, 4, ), VA_IF_ELSE(5, 6, ), ) == 6);
assert_se(VA_IF_ELSE(VA_IF_ELSE(3, 4, ), VA_IF_ELSE(5, 6, ), 1) == 4);
assert_se(VA_IF_ELSE(VA_IF_ELSE(3, 4, ), VA_IF_ELSE(5, 6, 1), ) == 5);
assert_se(VA_IF_ELSE(VA_IF_ELSE(3, 4, ), VA_IF_ELSE(5, 6, 1), 1) == 4);
assert_se(VA_IF_ELSE(VA_IF_ELSE(3, 4, 1), VA_IF_ELSE(5, 6, ), ) == 6);
assert_se(VA_IF_ELSE(VA_IF_ELSE(3, 4, 1), VA_IF_ELSE(5, 6, ), 1) == 3);
assert_se(VA_IF_ELSE(VA_IF_ELSE(3, 4, 1), VA_IF_ELSE(5, 6, 1), ) == 5);
assert_se(VA_IF_ELSE(VA_IF_ELSE(3, 4, 1), VA_IF_ELSE(5, 6, 1), 1) == 3);
}
TEST(va_if) {
assert_se(VA_IF(123,1) == 123);
assert_se(VA_IF(1+,1) 0 == 1);
assert_se(VA_IF(1+,0) 0 == 1);
assert_se(VA_IF(1+,) 0 == 0);
assert_se(VA_IF(1+, )0 == 0);
assert_se(VA_IF(1+, VA_IF(2+, VA_IF(3+, 4))) 0 == 1);
assert_se(VA_IF(1+, VA_IF(2+, VA_IF(3+, ))) 0 == 0);
assert_se(VA_IF(1+, VA_IF(, VA_IF(3+, 4))) 0 == 0);
assert_se(streq(VA_IF("hi", VA_IF(x,1)) "", "hi"));
assert_se(!streq(VA_IF("hi", VA_IF(x,NONE)) "", "hi"));
}
TEST(va_if_not) {
assert_se(VA_IF_NOT(123,) == 123);
assert_se(VA_IF_NOT(1+,1) 0 == 0);
assert_se(VA_IF_NOT(1+,0) 0 == 0);
assert_se(VA_IF_NOT(1+,) 0 == 1);
assert_se(VA_IF_NOT(1+, )0 == 1);
assert_se(VA_IF_NOT(1+, VA_IF_NOT(2+, VA_IF_NOT(3+, 4))) 0 == 0);
assert_se(VA_IF_NOT(1+, VA_IF_NOT(2+, VA_IF_NOT(3+, ))) 0 == 1);
assert_se(VA_IF_NOT(1+, VA_IF_NOT(, VA_IF_NOT(3+, 4))) 0 == 1);
assert_se(!streq(VA_IF_NOT("hi", 1) "", "hi"));
assert_se(streq(VA_IF_NOT("hi", NONE) "", "hi"));
}
TEST(va_not) {
assert_se(streq(STRINGIFY(VA_NOT()), "1"));
assert_se(streq(STRINGIFY(VA_NOT( )), "1"));
assert_se(streq(STRINGIFY(VA_NOT(1)), ""));
assert_se(streq(STRINGIFY(VA_NOT(0)), ""));
assert_se(streq(STRINGIFY(VA_NOT(1,2,3)), ""));
assert_se(streq(STRINGIFY(VA_NOT(,1,)), ""));
assert_se(streq(STRINGIFY(VA_NOT(,1)), ""));
assert_se(streq(STRINGIFY(VA_NOT("")), ""));
assert_se(streq(STRINGIFY(VA_NOT("hi")), ""));
assert_se(streq(STRINGIFY(VA_NOT(VA_NOT())), ""));
assert_se(streq(STRINGIFY(VA_NOT(VA_NOT(2))), "1"));
assert_se(streq(STRINGIFY(VA_NOT(VA_NOT("hi"))), "1"));
assert_se(streq(STRINGIFY(VA_NOT(VA_NOT(VA_NOT(2)))), ""));
assert_se(streq(STRINGIFY(VA_NOT(VA_NOT(2),VA_NOT(3))), ""));
assert_se(streq(STRINGIFY(VA_NOT(VA_NOT(),VA_NOT(3))), ""));
assert_se(streq(STRINGIFY(VA_NOT(VA_NOT(2),VA_NOT())), ""));
}
TEST(va_first) {
assert_se(VA_FIRST(1,2,3) == 1);
assert_se(VA_FIRST(1+,2+) 0 == 1);
assert_se(VA_FIRST(1+) 0 == 1);
assert_se(VA_FIRST() 0 == 0);
assert_se(streq(STRINGIFY(VA_FIRST()), ""));
assert_se(streq(STRINGIFY(VA_FIRST( )), ""));
assert_se(streq(STRINGIFY(VA_FIRST(,)), ""));
assert_se(streq(STRINGIFY(VA_FIRST(NONE)), ""));
assert_se(streq(STRINGIFY(VA_FIRST( NONE )), ""));
assert_se(streq(STRINGIFY(VA_FIRST( NONE, )), ""));
assert_se(streq(STRINGIFY(VA_FIRST( NONE,1,3 )), ""));
}
TEST(va_rest) {
assert_se(VA_REST(1,3) == 3);
assert_se(VA_REST(1+,2+) 0 == 2);
assert_se(VA_REST(1+) 0 == 0);
assert_se(VA_REST() 0 == 0);
assert_se(streq(STRINGIFY(VA_REST(NONE,1)), "1"));
assert_se(streq(STRINGIFY(VA_REST(1,NONE,1)), ",1"));
assert_se(streq(STRINGIFY(VA_REST(1,NONE)), ""));
assert_se(VA_FIRST(VA_REST(1,2,3,4,5)) == 2);
int ia[] = { VA_REST(1,2,3,4,5) };
assert_se(ELEMENTSOF(ia) == 4);
assert_se(ia[0] == 2);
assert_se(ia[1] == 3);
assert_se(ia[2] == 4);
assert_se(ia[3] == 5);
}
TEST(va_comma) {
assert_se(streq("0 , 1, 2", STRINGIFY(0 VA_COMMA(0) 1, 2)));
assert_se(streq("0 , 1, 2", STRINGIFY(0 VA_COMMA(1) 1, 2)));
assert_se(streq("0 1, 2", STRINGIFY(0 VA_COMMA() 1, 2)));
}
TEST(va_and) {
assert_se(streq(STRINGIFY(VA_AND(1,2)), "1"));
assert_se(streq(STRINGIFY(VA_AND(,2)), ""));
assert_se(streq(STRINGIFY(VA_AND(1,)), ""));
assert_se(streq(STRINGIFY(VA_AND(,)), ""));
assert_se(streq(STRINGIFY(VA_AND( , )), ""));
assert_se(streq(STRINGIFY(VA_AND(1 , )), ""));
assert_se(streq(STRINGIFY(VA_AND( , 2 )), ""));
assert_se(streq(STRINGIFY(VA_AND( 1 , 2 )), "1"));
assert_se(streq(STRINGIFY(VA_AND("hi",2)), "1"));
assert_se(streq(STRINGIFY(VA_AND(1,"hi")), "1"));
assert_se(streq(STRINGIFY(VA_AND("hi","hi")), "1"));
assert_se(streq(STRINGIFY(VA_AND(VA_AND(1,2),2)), "1"));
assert_se(streq(STRINGIFY(VA_AND(VA_AND(1,),2)), ""));
assert_se(streq(STRINGIFY(VA_AND(VA_AND(1,2),)), ""));
assert_se(streq(STRINGIFY(VA_AND( VA_AND( , 1 ) , VA_AND( , ) )), ""));
assert_se(streq(STRINGIFY(VA_AND( VA_AND( , ) , VA_AND( , ) )), ""));
}
TEST(va_or) {
assert_se(streq(STRINGIFY(VA_OR(1,2)), "1"));
assert_se(streq(STRINGIFY(VA_OR(,2)), "1"));
assert_se(streq(STRINGIFY(VA_OR(1,)), "1"));
assert_se(streq(STRINGIFY(VA_OR(,)), ""));
assert_se(streq(STRINGIFY(VA_OR("hi",2)), "1"));
assert_se(streq(STRINGIFY(VA_OR(1,"hi")), "1"));
assert_se(streq(STRINGIFY(VA_OR("hi","hi")), "1"));
assert_se(streq(STRINGIFY(VA_OR("hi",)), "1"));
assert_se(streq(STRINGIFY(VA_OR(,"hi")), "1"));
assert_se(streq(STRINGIFY(VA_OR( , )), ""));
assert_se(streq(STRINGIFY(VA_OR(VA_OR(1,),)), "1"));
assert_se(streq(STRINGIFY(VA_OR(VA_OR(,),)), ""));
assert_se(streq(STRINGIFY(VA_OR(VA_OR(,),2)), "1"));
assert_se(streq(STRINGIFY(VA_OR( VA_OR(1,) , )), "1"));
assert_se(streq(STRINGIFY(VA_OR( VA_OR( , 1 ) , VA_OR( , ) )), "1"));
assert_se(streq(STRINGIFY(VA_OR( VA_OR( , ) , VA_OR( , ) )), ""));
}
TEST(va_macro) {
assert_se(VA_MACRO(MACRO1, 3,2,1) == 3);
assert_se(VA_MACRO(MACRO1, 4) == 4);
assert_se(VA_MACRO(MACRO2, 4,5) == 5);
assert_se(streq(VA_MACRO(MACRO2, 4,"hi"), "hi"));
}
#define VA_NARGS_MAX_LESS_1 \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,010, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,020, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,030, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,040, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,050, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,060, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,070, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,080, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,090, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,0a0, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,0b0, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,0c0, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,0d0, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,0e0, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,0f0, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,100, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,110, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,120, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,130, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,140, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,150, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,160, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,170, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,180, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,190, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,1a0, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,1b0, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,1c0, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,1d0, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,1e0, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,1f0, \
1,2,3,4,5,6,7,8,9,a,b,c,d,e, 1ff
#define TEST_EQ_STR(expected, result) assert_se(streq(expected, STRINGIFY(result)))
#define XvX(c, i, v, ...) X v X
TEST(va_macro_foreach) {
TEST_EQ_STR("", VA_MACRO_FOREACH(XvX));
TEST_EQ_STR("", VA_MACRO_FOREACH(XvX,));
TEST_EQ_STR("", VA_MACRO_FOREACH(XvX, ));
TEST_EQ_STR("X 1 X", VA_MACRO_FOREACH(XvX, 1));
TEST_EQ_STR("X 1 X, X 2 X", VA_MACRO_FOREACH(XvX, 1, 2));
TEST_EQ_STR("X hi X", VA_MACRO_FOREACH(XvX, hi));
TEST_EQ_STR("X one X, X two X, X three X", VA_MACRO_FOREACH(XvX, one, two, three));
TEST_EQ_STR("X 1 X, X 2 X, X X, X 4 X, X 5 X", VA_MACRO_FOREACH(XvX, 1, 2, , 4, 5));
TEST_EQ_STR("X X, X 2 X, X 3 X, X 4 X, X 5 X", VA_MACRO_FOREACH(XvX, , 2, 3, 4, 5));
/* Note that if the final arg is empty (or only whitespace), it is not included. */
TEST_EQ_STR("X 1 X", VA_MACRO_FOREACH(XvX, 1,));
TEST_EQ_STR("X X, X 1 X", VA_MACRO_FOREACH(XvX, ,1));
TEST_EQ_STR("X 1 X", VA_MACRO_FOREACH(XvX, 1, ));
TEST_EQ_STR("X X, X 1 X", VA_MACRO_FOREACH(XvX,,1));
TEST_EQ_STR("X X, X 1 X", VA_MACRO_FOREACH(XvX,,1,));
TEST_EQ_STR("X X, X 1 X", VA_MACRO_FOREACH(XvX,,1, ));
TEST_EQ_STR("X X, X X, X X, X X", VA_MACRO_FOREACH(XvX, , , , , ));
TEST_EQ_STR("X X, X X, X X, X X, X 1 X", VA_MACRO_FOREACH(XvX, , , , , 1));
TEST_EQ_STR("X X, X X, X X, X X, X 1 X", VA_MACRO_FOREACH(XvX, , , , ,1));
TEST_EQ_STR("X X, X X, X X, X X", VA_MACRO_FOREACH(XvX,,,,,));
TEST_EQ_STR("X X, X X, X X, X X, X 1 X", VA_MACRO_FOREACH(XvX,,,,,1));
TEST_EQ_STR("X X, X X, X X, X X, X 1 X", VA_MACRO_FOREACH(XvX,,,,, 1));
}
TEST(va_filter) {
TEST_EQ_STR("0, 1, 2, 3, hi, later", VA_FILTER(0, 1, 2, 3, , , , hi, later, ));
TEST_EQ_STR("", VA_FILTER(, , , , ,));
TEST_EQ_STR("5", VA_FILTER(, , , , ,5));
TEST_EQ_STR("4, 5", VA_FILTER(4, , , , ,5));
TEST_EQ_STR("6, 7", VA_FILTER(, 6, 7, , ,));
TEST_EQ_STR("\"one\", \"two\"", VA_FILTER(, "one", ,"two" , ,));
}
#define TEST_NARGS(expect, expect_token, ...) \
({ \
assert_se(VA_NARGS(__VA_ARGS__) == expect); \
assert_se(streq(STRINGIFY(expect_token), STRINGIFY(VA_NARGS(__VA_ARGS__)))); \
assert_se(__builtin_constant_p(VA_NARGS(__VA_ARGS__))); \
})
TEST(va_nargs) {
_unused_ int i = 0;
_unused_ const char *hi = "hello";
TEST_NARGS(0, 0x0000);
TEST_NARGS(0, 0x0000,);
TEST_NARGS(0, 0x0000, );
TEST_NARGS(1, 0x0001, 1);
TEST_NARGS(1, 0x0001, "hello");
TEST_NARGS(1, 0x0001, "hello");
TEST_NARGS(1, 0x0001, i);
TEST_NARGS(1, 0x0001, i++);
TEST_NARGS(2, 0x0002, i, hi);
TEST_NARGS(16, 0x0010, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
}
TEST(va_last) {
_unused_ int i = 0;
_unused_ const char *hi = "hello";
assert_se(streq(STRINGIFY(VA_LAST()), ""));
assert_se(VA_LAST(1,2,10) == 10);
assert_se(streq(VA_LAST("hi", "there"), "there"));
assert_se(VA_LAST(1,2,i++) == 0);
assert_se(i == 1);
assert_se(VA_LAST(1,2,++i) == 2);
assert_se(i == 2);
assert_se(VA_LAST(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) == 15);
assert_se(VA_LAST(VA_NARGS_MAX_LESS_1,123) == 123);
}
TEST(va_declarations) {
int i = 999;
VA_DECLARATIONS(test_decl, int, char*, uint64_t, typeof(i));
test_decl_0x0001 = 10;
test_decl_0x0002 = (char*)"hello";
test_decl_0x0003 = 0xffff000000000001;
test_decl_0x0004 = 8;
assert_se(test_decl_0x0001 == 10);
assert_se(__builtin_types_compatible_p(typeof(test_decl_0x0001), int));
assert_se(streq(test_decl_0x0002, "hello"));
assert_se(__builtin_types_compatible_p(typeof(test_decl_0x0002), char*));
assert_se(test_decl_0x0003 == 0xffff000000000001);
assert_se(__builtin_types_compatible_p(typeof(test_decl_0x0003), uint64_t));
assert_se(test_decl_0x0004 == 8);
assert_se(__builtin_types_compatible_p(typeof(test_decl_0x0004), int));
VA_DECLARATIONS();
VA_INITIALIZED_DECLARATIONS(test_i, test_decl_0x0003, test_decl_0x0004, i, test_decl_0x0002, test_decl_0x0001, i);
assert_se(__builtin_types_compatible_p(typeof(test_i_0x0001), uint64_t));
assert_se(test_i_0x0001 == 0xffff000000000001);
test_i_0x0001--;
assert_se(test_i_0x0001 == 0xffff000000000000);
assert_se(test_decl_0x0003 == 0xffff000000000001);
test_decl_0x0003 = 0xffff;
assert_se(test_i_0x0001 == 0xffff000000000000);
assert_se(__builtin_types_compatible_p(typeof(test_i_0x0002), int));
assert_se(test_i_0x0002 == 8);
test_i_0x0002--;
assert_se(test_i_0x0002 == 7);
assert_se(test_decl_0x0004 == 8);
test_decl_0x0004 = 50;
assert_se(test_i_0x0002 == 7);
assert_se(__builtin_types_compatible_p(typeof(test_i_0x0003), int));
assert_se(test_i_0x0003 == 999);
test_i_0x0003--;
assert_se(test_i_0x0003 == 998);
assert_se(i == 999);
i = 333;
assert_se(test_i_0x0003 == 998);
assert_se(__builtin_types_compatible_p(typeof(test_i_0x0004), char*));
assert_se(streq(test_i_0x0004, "hello"));
assert_se(streq(test_i_0x0004, test_decl_0x0002));
test_i_0x0004 = NULL;
assert_se(test_i_0x0004 == NULL);
assert_se(streq(test_decl_0x0002, "hello"));
assert_se(__builtin_types_compatible_p(typeof(test_i_0x0005), int));
assert_se(test_i_0x0005 == 10);
test_i_0x0005--;
assert_se(test_i_0x0005 == 9);
assert_se(test_decl_0x0001 == 10);
test_decl_0x0001 = 44;
assert_se(test_i_0x0005 == 9);
assert_se(__builtin_types_compatible_p(typeof(test_i_0x0006), int));
assert_se(test_i_0x0006 == 999);
test_i_0x0006--;
assert_se(test_i_0x0006 == 998);
assert_se(i == 333);
i = 222;
assert_se(test_i_0x0006 == 998);
VA_INITIALIZED_DECLARATIONS();
}
#define TEST_TOKENS(equal1, equal2, equal3, equal4, \
expect1, expect2, expect3, expect4, \
v1, v2, v3, v4) \
({ \
assert_se((expect1 == v1) == equal1); \
assert_se((expect2 == v2) == equal2); \
assert_se((expect3 == v3) == equal3); \
assert_se((expect4 == v4) == equal4); \
})
TEST(va_tokens) {
int i1 = 10, i2 = 100, i3 = 50, i4 = 99;
VA_INITIALIZED_DECLARATIONS(test_i_, i1, i2, i3, i4);
VA_MACRO(TEST_TOKENS, true, true, true, true, i1, i2, i3, i4, VA_TOKENS(test_i_, i1, i2, i3, i4));
VA_MACRO(TEST_TOKENS, true, true, true, true, 10, 100, i3, 99, VA_TOKENS(test_i_, i1, i2, i3, i4));
/* VA_TOKENS() doesn't actually use the variadic args, the tokens are based on index */
VA_MACRO(TEST_TOKENS, true, true, true, true, i1, i2, i3, i4, VA_TOKENS(test_i_, x, x, x, x));
VA_MACRO(TEST_TOKENS, true, false, true, false, i1, i4, i3, 1234, VA_TOKENS(test_i_, i1, i2, i3, i4));
}
#define TEST_UNIQ(x, y, z) \
_unused_ int x = 10; \
_unused_ const char *y = "hi"; \
_unused_ uint64_t z = 0xffff;
TEST(va_uniq) {
int x = 20;
const char *y = "still me";
uint64_t z = 0xf;
VA_MACRO(TEST_UNIQ, VA_UNIQ(first, second, third));
assert_se(x == 20);
assert_se(streq(y, "still me"));
assert_se(z == 0xf);
}
#define TEST_MACRO_SWAP(tmp, x, y) \
({ \
tmp = x; \
x = y; \
y = tmp; \
})
#define TEST_MACRO_ALL(u1, u2, v1, v2, vi1, vi2, vc1, vc2, d1, d2) \
({ \
int u1 = 100; \
char *u2 = (char*)"u2"; \
assert_se(u1 == 100); \
assert_se(streq(u2, "u2")); \
\
v1 = d1; \
v2 = d2; \
assert_se(v1 == 30); \
assert_se(streq(v2, "d2")); \
v1++; \
v2++; \
assert_se(v1 == 31); \
assert_se(streq(v2, "2")); \
\
assert_se(vi1 == 10); \
assert_se(streq(vi2, "vi2")); \
vi1++; \
vi2++; \
assert_se(vi1 == 11); \
assert_se(streq(vi2, "i2")); \
\
assert_se(vc1 == 20); \
assert_se(streq(vc2, "vc2")); \
\
assert_se(d1 == 30); \
assert_se(streq(d2, "d2")); \
\
d1 = u1; \
d2 = u2; \
assert_se(d1 == 100); \
assert_se(streq(d2, "u2")); \
\
d1 + 1000; \
})
TEST(va_macro_helper) {
int i1, i2;
i1 = 10;
i2 = 20;
VA_MACRO_HELPER(TEST_MACRO_SWAP,
/*uniq*/,
int,
/*varinit*/,
/*varconst*/,
VA_GROUP(i1, i2));
assert_se(i1 == 20);
assert_se(i2 == 10);
int vi1 = 10, vc1 = 20, d1 = 30;
char *vi2 = (char*)"vi2", *vc2 = (char*)"vc2", *d2 = (char*)"d2";
int all = VA_MACRO_HELPER(TEST_MACRO_ALL,
VA_GROUP(u1, u2),
VA_GROUP(int, char*),
VA_GROUP(vi1, vi2),
VA_GROUP(vc1, vc2),
VA_GROUP(d1, d2));
assert_se(all == 1100);
assert_se(vi1 == 10);
assert_se(streq(vi2, "vi2"));
assert_se(vc1 == 20);
assert_se(streq(vc2, "vc2"));
assert_se(d1 == 100);
assert_se(streq(d2, "u2"));
}
#define TEST_UNIQ_INT_X(_x) \
({ \
int _x = 5; \
_x++; \
})
#define TEST_UNIQ_INT_X_Y_Z(x, y, z, v, ...) \
({ \
int x = v; \
int y = VA_IF_ELSE(VA_FIRST(__VA_ARGS__), 100, __VA_ARGS__); \
int z = VA_IF_ELSE(VA_FIRST(VA_REST(__VA_ARGS__)), 2000, VA_REST(__VA_ARGS__)); \
x + y + z; \
})
TEST(va_macro_uniq) {
int x = 1, _x = 2;
int y = VA_MACRO_UNIQ(TEST_UNIQ_INT_X, _x);
assert_se(x == 1);
assert_se(_x == 2);
assert_se(y == 5);
int z = VA_MACRO_UNIQ(TEST_UNIQ_INT_X_Y_Z, VA_GROUP(x, y, z), x);
assert_se(x == 1);
assert_se(_x == 2);
assert_se(y == 5);
assert_se(z == 2101);
_x = VA_MACRO_UNIQ(TEST_UNIQ_INT_X_Y_Z, VA_GROUP(1, 2, z), 99);
assert_se(x == 1);
assert_se(_x == 2199);
assert_se(y == 5);
assert_se(z == 2101);
z = VA_MACRO_UNIQ(TEST_UNIQ_INT_X_Y_Z, VA_GROUP(_X, _Y, _Z), 5, 20);
assert_se(x == 1);
assert_se(_x == 2199);
assert_se(y == 5);
assert_se(z == 2025);
z = VA_MACRO_UNIQ(TEST_UNIQ_INT_X_Y_Z, VA_GROUP(_X, _Y, _Z), 7, 70, 5000);
assert_se(x == 1);
assert_se(_x == 2199);
assert_se(y == 5);
assert_se(z == 5077);
}
#define TEST_MACRO_INT_CHARP(x1, x2) \
({ \
assert_se(__builtin_types_compatible_p(typeof(x1), int)); \
assert_se(__builtin_types_compatible_p(typeof(x2), char*)); \
})
typedef struct { int a; } structabc;
#define TEST_MACRO_INTP_STRUCTABC_INT(x1, x2, x3) \
({ \
assert_se(__builtin_types_compatible_p(typeof(x1), int*)); \
assert_se(__builtin_types_compatible_p(typeof(x2), structabc)); \
assert_se(__builtin_types_compatible_p(typeof(x3), int)); \
})
#define TEST_MACRO_INT_TMP1(x) \
({ \
x = 7; \
x++; \
})
TEST(va_macro_var) {
int j = VA_MACRO_VAR(TEST_MACRO_INT_TMP1, int);
assert_se(j == 7);
assert_se(VA_MACRO_VAR(TEST_MACRO_INT_TMP1, int) == 7);
VA_MACRO_VAR(TEST_MACRO_INT_CHARP, VA_GROUP(int, char*));
VA_MACRO_VAR(TEST_MACRO_INTP_STRUCTABC_INT, VA_GROUP(int*, structabc, int));
}
#define MACRO_USE_TWICE_1L2_OR_B0(x1, x2) \
({ \
(x1 < x2) || (x1 == 0 && x2 == 0); \
})
#define MACRO_INT_USE_ARGS1_EVAL1(x1) \
({ \
_unused_ int x = x1; \
x1; \
})
#define MACRO_INT_USE_ARGS2_EVAL1(x1,x2) \
({ \
_unused_ int x = x1 + x2; \
x1; \
})
#define MACRO_INT_USE_ARGS2_EVAL2(x1,x2) \
({ \
_unused_ int x = x1 + x2; \
x2; \
})
#define MACRO_INT_USE_ARGS6_EVAL1(x1,x2,x3,x4,x5,x6) \
({ \
_unused_ int x = x1 + x2 + x3 + x4 + x5 + x6; \
x1; \
})
#define MACRO_INT_USE_ARGS6_EVAL4(x1,x2,x3,x4,x5,x6) \
({ \
_unused_ int x = x1 + x2 + x3 + x4 + x5 + x6; \
x4; \
})
TEST(va_macro_varinit) {
_unused_ int i = 1, j = 0, k = 5678;
assert_se(VA_MACRO_VARINIT(MACRO_INT_USE_ARGS1_EVAL1, 1) == 1);
assert_se(VA_MACRO_VARINIT(MACRO_INT_USE_ARGS2_EVAL1, VA_GROUP(1, 10)) == 1);
assert_se(VA_MACRO_VARINIT(MACRO_INT_USE_ARGS2_EVAL2, VA_GROUP(1, 10)) == 10);
assert_se(VA_MACRO_VARINIT(MACRO_INT_USE_ARGS6_EVAL1, VA_GROUP(100, 1000, 1, 0, 20, -80)) == 100);
assert_se(VA_MACRO_VARINIT(MACRO_INT_USE_ARGS6_EVAL4, VA_GROUP(-9,i,1,k,3,4)) == 5678);
assert_se(VA_MACRO_VARINIT(MACRO_SUM12, VA_GROUP(1,10)) == 11);
assert_se(VA_MACRO_VARINIT(MACRO_SUM12, VA_GROUP(10,k)) == 5688);
i = 1234;
assert_se(VA_MACRO_VARINIT(MACRO1, i) == 1234);
assert_se(VA_MACRO_VARINIT(MACRO1, 1234) == i);
i = 10;
j = 20;
assert_se(VA_MACRO_VARINIT(MACRO_USE_TWICE_1L2_OR_B0, VA_GROUP(i++, j--)) == 1);
assert_se(i == 11);
assert_se(j == 19);
assert_se(VA_MACRO_VARINIT(MACRO_USE_TWICE_1L2_OR_B0, VA_GROUP(j + 5, j + 10)) == 1);
assert_se(i == 11);
assert_se(j == 19);
i = 10;
j = 0;
assert_se(VA_MACRO_VARINIT(MACRO_USE_TWICE_1L2_OR_B0, VA_GROUP(i - 10, j)) == 1);
assert_se(i == 10);
assert_se(j == 0);
assert_se(VA_MACRO_VARINIT(MACRO_USE_TWICE_1L2_OR_B0, VA_GROUP(i, j--)) == 0);
assert_se(i == 10);
assert_se(j == -1);
}
TEST(va_macro_varconst) {
_unused_ int i = 1, j = 0, k = 4444;
assert_se(VA_MACRO_VARCONST(MACRO_INT_USE_ARGS1_EVAL1, 1) == 1);
assert_se(VA_MACRO_VARCONST(MACRO_INT_USE_ARGS2_EVAL2, VA_GROUP(1, 10)) == 10);
assert_se(VA_MACRO_VARCONST(MACRO_INT_USE_ARGS6_EVAL4, VA_GROUP(0,i,1,k,3,4)) == 4444);
assert_se(VA_MACRO_VARCONST(MACRO_INT_USE_ARGS6_EVAL1, VA_GROUP(i,2,2,3,4,k)) == 1);
assert_se(VA_MACRO_VARCONST(MACRO_INT_USE_ARGS6_EVAL1, VA_GROUP(1000,2,3,4,k,0)) == 1000);
assert_se(VA_MACRO_VARCONST(MACRO_SUM12, VA_GROUP(1,10)) == 11);
assert_se(VA_MACRO_VARCONST(MACRO_SUM12, VA_GROUP(k,1)) == 4445);
i = 1234;
assert_se(VA_MACRO_VARCONST(MACRO_INT_USE_ARGS1_EVAL1, i) == 1234);
assert_se(VA_MACRO_VARCONST(MACRO_INT_USE_ARGS1_EVAL1, 1234) == i);
}
TEST(va_toomany) {
/* Test assumes largest __VA_EVAL_0x*() macro is 0x0200. */
assert_se(VA_NARGS(VA_NARGS_MAX_LESS_1) == 0x1ff);
assert_se(VA_NARGS(VA_NARGS_MAX_LESS_1,1) == 0x200);
assert_se(VA_WRAP(VA_WRAP_MACRO_LAST,
VA_WRAP_SEPARATOR_NONE,
-1,
VA_WRAP_ZERO_NONE,
VA_WRAP_TOOMANY_CONTEXT,
VA_NARGS_MAX_LESS_1, -2) == -2);
assert_se(VA_WRAP(VA_WRAP_MACRO_LAST,
VA_WRAP_SEPARATOR_NONE,
-1,
VA_WRAP_ZERO_NONE,
VA_WRAP_TOOMANY_CONTEXT,
VA_NARGS_MAX_LESS_1, -2, -3) == -1);
}
TEST(va_number) {
assert_se(___VANUM4(4,3,2,1) == 0x4321);
assert_se(___VANUM4(f,f,f,f) == 0xffff);
assert_se(___VANUM4(0,0,0,0) == 0);
assert_se(___VANUM4(0,0,0,1) == 1);
assert_se(___VANUM4(0,1,0,0) == 0x100);
assert_se(___VANUM4(1,0,0,1) == 0x1001);
assert_se(__VANUM4((1,0,0,1)) == 0x1001);
}
TEST(va_inc) {
assert_se(__VANUM4(__VAINC4((1,2,3,4))) == 0x1235);
assert_se(__VANUM4(__VAINC4((0,0,0,0))) == 1);
assert_se(__VANUM4(__VAINC4((0,0,0,1))) == 2);
assert_se(__VANUM4(__VAINC4((1,0,0,0))) == 0x1001);
assert_se(__VANUM4(__VAINC4((f,f,f,e))) == 0xffff);
assert_se(__VANUM4(__VAINC4((e,f,f,e))) == 0xefff);
assert_se(__VANUM4(__VAINC4((e,f,e,f))) == 0xeff0);
assert_se(__VANUM4(__VAINC4((d,f,f,f))) == 0xe000);
}
DEFINE_TEST_MAIN(LOG_INFO);

View File

@ -526,9 +526,6 @@ def call_systemd_measure(uki, linux, opts):
# First, pick up the sections we shall measure now */
for s in uki.sections:
print(s)
if not s.measure:
continue

View File

@ -0,0 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
all setup run clean clean-again:
true
.PHONY: all setup run clean clean-again

View File

@ -0,0 +1,10 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
integration_tests += [
integration_test_template + {
'name' : fs.name(meson.current_source_dir()),
'storage' : 'persistent',
'vm' : true,
'firmware' : 'auto',
},
]

View File

@ -376,6 +376,7 @@ foreach dirname : [
'TEST-83-BTRFS',
'TEST-84-STORAGETM',
'TEST-85-NETWORK',
'TEST-86-MULTI-PROFILE-UKI',
]
subdir(dirname)
endforeach

View File

@ -29,6 +29,9 @@ if ! systemd-detect-virt --quiet --container; then
udevadm control --log-level debug
fi
esp_guid=C12A7328-F81F-11D2-BA4B-00A0C93EC93B
xbootldr_guid=BC13C2FF-59E6-4262-A352-B275FD6F7172
machine="$(uname -m)"
if [ "${machine}" = "x86_64" ]; then
root_guid=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709
@ -1432,6 +1435,82 @@ EOF
systemd-dissect -U "$imgs/mnt"
}
testcase_fallback_partitions() {
local workdir image defs
workdir="$(mktemp --directory "/tmp/test-repart.fallback.XXXXXXXXXX")"
# shellcheck disable=SC2064
trap "rm -rf '${workdir:?}'" RETURN
image="$workdir/image.img"
defs="$workdir/defs"
mkdir "$defs"
tee "$defs/10-esp.conf" <<EOF
[Partition]
Type=esp
Format=vfat
SizeMinBytes=10M
EOF
tee "$defs/20-xbootldr.conf" <<EOF
[Partition]
Type=xbootldr
Format=vfat
SizeMinBytes=100M
SupplementFor=10-esp
EOF
# Blank disk => big ESP should be created
systemd-repart --empty=create --size=auto --dry-run=no --definitions="$defs" "$image"
output=$(sfdisk -d "$image")
assert_in "${image}1 : start= 2048, size= 204800, type=${esp_guid}" "$output"
assert_not_in "${image}2" "$output"
# Disk with small ESP => ESP grows
sfdisk "$image" <<EOF
label: gpt
size=10M, type=${esp_guid}
EOF
systemd-repart --dry-run=no --definitions="$defs" "$image"
output=$(sfdisk -d "$image")
assert_in "${image}1 : start= 2048, size= 204800, type=${esp_guid}" "$output"
assert_not_in "${image}2" "$output"
# Disk with small ESP that can't grow => XBOOTLDR created
truncate -s 150M "$image"
sfdisk "$image" <<EOF
label: gpt
size=10M, type=${esp_guid},
size=10M, type=${root_guid},
EOF
systemd-repart --dry-run=no --definitions="$defs" "$image"
output=$(sfdisk -d "$image")
assert_in "${image}1 : start= 2048, size= 20480, type=${esp_guid}" "$output"
assert_in "${image}3 : start= 43008, size= 264152, type=${xbootldr_guid}" "$output"
# Disk with existing XBOOTLDR partition => XBOOTLDR grows, small ESP created
sfdisk "$image" <<EOF
label: gpt
size=10M, type=${xbootldr_guid},
EOF
systemd-repart --dry-run=no --definitions="$defs" "$image"
output=$(sfdisk -d "$image")
assert_in "${image}1 : start= 2048, size= 204800, type=${xbootldr_guid}" "$output"
assert_in "${image}2 : start= 206848, size= 100312, type=${esp_guid}" "$output"
}
OFFLINE="yes"
run_testcases

View File

@ -0,0 +1,81 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
export SYSTEMD_LOG_LEVEL=debug
bootctl
CURRENT_UKI=$(bootctl --print-stub-path)
echo "CURRENT UKI ($CURRENT_UKI):"
ukify inspect "$CURRENT_UKI"
if test -f /run/systemd/stub/profile; then
echo "CURRENT PROFILE:"
cat /run/systemd/stub/profile
fi
echo "CURRENT MEASUREMENT:"
/usr/lib/systemd/systemd-measure --current
if test -f /run/systemd/tpm2-pcr-signature.json; then
echo "CURRENT SIGNATURE:"
jq </run/systemd/tpm2-pcr-signature.json
fi
echo "CURRENT EVENT LOG + PCRS:"
/usr/lib/systemd/systemd-pcrlock
if test ! -f /run/systemd/stub/profile; then
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out /root/pcrsign.private.pem
openssl rsa -pubout -in /root/pcrsign.private.pem -out /root/pcrsign.public.pem
ukify build --extend="$CURRENT_UKI" --output=/tmp/extended0.efi --profile='ID=profile0
TITLE="Profile Zero"' --measure-base="$CURRENT_UKI" --pcr-private-key=/root/pcrsign.private.pem --pcr-public-key=/root/pcrsign.public.pem --pcr-banks=sha256,sha384,sha512
ukify build --extend=/tmp/extended0.efi --output=/tmp/extended1.efi --profile='ID=profile1
TITLE="Profile One"' --measure-base=/tmp/extended0.efi --cmdline="testprofile1=1 $(cat /proc/cmdline)" --pcr-private-key=/root/pcrsign.private.pem --pcr-public-key=/root/pcrsign.public.pem --pcr-banks=sha256,sha384,sha512
ukify build --extend=/tmp/extended1.efi --output=/tmp/extended2.efi --profile='ID=profile2
TITLE="Profile Two"' --measure-base=/tmp/extended1.efi --cmdline="testprofile2=1 $(cat /proc/cmdline)" --pcr-private-key=/root/pcrsign.private.pem --pcr-public-key=/root/pcrsign.public.pem --pcr-banks=sha256,sha384,sha512
echo "EXTENDED UKI:"
ukify inspect /tmp/extended2.efi
rm /tmp/extended0.efi /tmp/extended1.efi
mv /tmp/extended2.efi "$CURRENT_UKI"
# Prepare a disk image, locked to the PCR measurements of the UKI we just generated
truncate -s 32M /root/encrypted.raw
echo -n "geheim" >/root/encrypted.secret
cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom /root/encrypted.raw --key-file=/root/encrypted.secret
systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs= --tpm2-public-key=/root/pcrsign.public.pem --unlock-key-file=/root/encrypted.secret /root/encrypted.raw
rm -f /root/encrypted.secret
reboot
exit 0
else
# shellcheck source=/dev/null
. /run/systemd/stub/profile
# Validate that with the current profile we can fulfill the PCR 11 policy
systemd-cryptsetup attach multiprof /root/encrypted.raw - tpm2-device=auto,headless=1
systemd-cryptsetup detach multiprof
if [ "$ID" = "profile0" ]; then
grep -v testprofile /proc/cmdline
echo "default $(basename "$CURRENT_UKI")@profile1" >"$(bootctl -p)/loader/loader.conf"
reboot
exit 0
elif [ "$ID" = "profile1" ]; then
grep testprofile1=1 /proc/cmdline
echo "default $(basename "$CURRENT_UKI")@profile2" >"$(bootctl -p)/loader/loader.conf"
reboot
exit 0
elif [ "$ID" = "profile2" ]; then
grep testprofile2=1 /proc/cmdline
rm /root/encrypted.raw
else
exit 1
fi
fi
touch /testok

View File

@ -19,5 +19,5 @@ Q /var/lib/machines 0700 - - -
# systemd-nspawn --ephemeral places snapshots) we are more strict, to
# avoid removing unrelated temporary files.
R!$ /var/lib/machines/.#*
R!$ /.#machine.*
R! /var/lib/machines/.#*
R! /.#machine.*

View File

@ -14,10 +14,10 @@ x /var/tmp/systemd-private-%b-*
X /var/tmp/systemd-private-%b-*/tmp
# Remove top-level private temporary directories on each boot
R!$ /tmp/systemd-private-*
R!$ /var/tmp/systemd-private-*
R! /tmp/systemd-private-*
R! /var/tmp/systemd-private-*
# Handle lost systemd-coredump temp files. They could be lost on old filesystems,
# for example, after hard reboot.
x /var/lib/systemd/coredump/.#core*.%b*
r!$ /var/lib/systemd/coredump/.#*
r! /var/lib/systemd/coredump/.#*