Compare commits
25 Commits
745120aad3
...
39c5ed0c91
Author | SHA1 | Date |
---|---|---|
Dan Streetman | 39c5ed0c91 | |
Yu Watanabe | 7ac1ad90d0 | |
Daan De Meyer | 099b16c3e7 | |
Daan De Meyer | 7a7f306b6c | |
Yu Watanabe | 4f2975385f | |
Daan De Meyer | 0432e28394 | |
Yu Watanabe | fc956a3973 | |
Yu Watanabe | d265b8afb7 | |
Yu Watanabe | 1aab0a5b10 | |
Yu Watanabe | b0dbb4aa3a | |
Michael Ferrari | 91ea3dcf35 | |
Yu Watanabe | a95ae2d36a | |
Yu Watanabe | be8e4b1a87 | |
Adrian Vovk | cf612c5fd5 | |
Adrian Vovk | 2cb9c68c3a | |
Adrian Vovk | 78e9059208 | |
Adrian Vovk | e671bdc5c3 | |
Yu Watanabe | 572d031eca | |
Yu Watanabe | 25da422bd1 | |
Yu Watanabe | 5872ea7008 | |
Lennart Poettering | a2369d0224 | |
Lennart Poettering | a37640653c | |
Dan Streetman | 413f6e0f56 | |
Dan Streetman | f3ab293474 | |
Dan Streetman | b595c40c72 |
2
TODO
2
TODO
|
@ -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.
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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_;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -2434,6 +2434,8 @@ static int create_interactively(void) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
any_key_to_proceed();
|
||||
|
||||
r = acquire_bus(&bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
r = append_btrfs_subvols(&sv, p->subvolumes, p->default_subvolume);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
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);
|
||||
if (p->suppressing) {
|
||||
r = append_btrfs_subvols(&sv, p->suppressing->subvolumes, NULL);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
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)) {
|
||||
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;
|
||||
}
|
||||
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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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);
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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',
|
||||
},
|
||||
]
|
|
@ -376,6 +376,7 @@ foreach dirname : [
|
|||
'TEST-83-BTRFS',
|
||||
'TEST-84-STORAGETM',
|
||||
'TEST-85-NETWORK',
|
||||
'TEST-86-MULTI-PROFILE-UKI',
|
||||
]
|
||||
subdir(dirname)
|
||||
endforeach
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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.*
|
||||
|
|
|
@ -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/.#*
|
||||
|
|
Loading…
Reference in New Issue