Compare commits

...

6 Commits

Author SHA1 Message Date
Federico Giovanardi 4518a8d188
Merge 9d58f3b81e into 9bf6ffe166 2024-11-22 13:01:34 +01:00
Luca Boccassi 9bf6ffe166
man: split cryptenroll man page into sections (#35297) 2024-11-22 12:01:07 +00:00
Lennart Poettering cc6baba720 cryptenroll: it's called PKCS#11, not PKCS11
In the --help text we really should use the official spelling, just like
in the man page.
2024-11-22 10:42:37 +01:00
Lennart Poettering 3ae48d071c man: add enrollment type sections to cryptenroll man page
We have the same sections in the --help text, hence we even more so
should have them in the man page.
2024-11-22 10:42:37 +01:00
Antonio Alvarez Feijoo 2ccacdd57c bash-completion: add --list-devices to systemd-cryptenroll
And also use it to list suitable block devices.
2024-11-22 10:38:19 +01:00
Federico Giovanardi 9d58f3b81e udev: add option to trigger parent devices despite filters
This commit add the `-i` option to `udevadm trigger` that force it to
match parent devices even if they're excluded from filters.
The rationale is that some embedded devices have a huge number of
platform devices ( ~ 4k for MX8 ) they are there because they're defined
in the device tree but there isn't any action or udev rules associated
with them.

So at boot a significant time is spend triggering and processing rules
for devices that don't produce any effect and we would like to filter
them by calling:

```
udevadm trigger --type=device --action=add -s block -s tty
```

instead of the normal

```
udevadm trigger --type=device --action=add
```

so we can use filter to filter out only subsystems for we we know that
we have rules in place that do something useful.

On the other side action / rules are not triggered until the parent is
triggered ( which is part of another subsystem), so the additional option
will allows udev to complete the coldplug with only the devices we care.

Example on iMX8:

.Without the new option
```
root@dev:~# udevadm trigger --dry-run  -s block --action=add -v
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0boot0
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0boot1
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p1
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p2
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p3
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p4
```

.With the new option
```
root@dev:~# udevadm trigger --dry-run -i -s block --action=add -v
/sys/devices/platform
/sys/devices/platform/bus@5b000000
/sys/devices/platform/bus@5b000000/5b010000.mmc
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0boot0
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0boot1
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p1
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p2
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p3
/sys/devices/platform/bus@5b000000/5b010000.mmc/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p4
```
Boot time reduction with this is place is ~ 1 second.
2024-11-21 09:00:51 +01:00
11 changed files with 163 additions and 53 deletions

View File

@ -265,32 +265,11 @@
</refsect1> </refsect1>
<refsect1> <refsect1>
<title>Options</title> <title>Unlocking</title>
<para>The following options are understood:</para> <para>The following options are understood that may be used to unlock the device in preparation of the enrollment operations:</para>
<variablelist> <variablelist>
<varlistentry>
<term><option>--password</option></term>
<listitem><para>Enroll a regular password/passphrase. This command is mostly equivalent to
<command>cryptsetup luksAddKey</command>, however may be combined with
<option>--wipe-slot=</option> in one call, see below.</para>
<xi:include href="version-info.xml" xpointer="v248"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--recovery-key</option></term>
<listitem><para>Enroll a recovery key. Recovery keys are mostly identical to passphrases, but are
computer-generated instead of being chosen by a human, and thus have a guaranteed high entropy. The
key uses a character set that is easy to type in, and may be scanned off screen via a QR code.
</para>
<xi:include href="version-info.xml" xpointer="v248"/></listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>--unlock-key-file=<replaceable>PATH</replaceable></option></term> <term><option>--unlock-key-file=<replaceable>PATH</replaceable></option></term>
@ -328,7 +307,45 @@
<xi:include href="version-info.xml" xpointer="v256"/></listitem> <xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry> </varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Simple Enrollment</title>
<para>The following options are understood that may be used to enroll simple user input based
unlocking:</para>
<variablelist>
<varlistentry>
<term><option>--password</option></term>
<listitem><para>Enroll a regular password/passphrase. This command is mostly equivalent to
<command>cryptsetup luksAddKey</command>, however may be combined with
<option>--wipe-slot=</option> in one call, see below.</para>
<xi:include href="version-info.xml" xpointer="v248"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--recovery-key</option></term>
<listitem><para>Enroll a recovery key. Recovery keys are mostly identical to passphrases, but are
computer-generated instead of being chosen by a human, and thus have a guaranteed high entropy. The
key uses a character set that is easy to type in, and may be scanned off screen via a QR code.
</para>
<xi:include href="version-info.xml" xpointer="v248"/></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>PKCS#11 Enrollment</title>
<para>The following option is understood that may be used to enroll PKCS#11 tokens:</para>
<variablelist>
<varlistentry> <varlistentry>
<term><option>--pkcs11-token-uri=<replaceable>URI</replaceable></option></term> <term><option>--pkcs11-token-uri=<replaceable>URI</replaceable></option></term>
@ -361,7 +378,15 @@
<xi:include href="version-info.xml" xpointer="v248"/></listitem> <xi:include href="version-info.xml" xpointer="v248"/></listitem>
</varlistentry> </varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>FIDO2 Enrollment</title>
<para>The following options are understood that may be used to enroll PKCS#11 tokens:</para>
<variablelist>
<varlistentry> <varlistentry>
<term><option>--fido2-credential-algorithm=<replaceable>STRING</replaceable></option></term> <term><option>--fido2-credential-algorithm=<replaceable>STRING</replaceable></option></term>
<listitem><para>Specify COSE algorithm used in credential generation. The default value is <listitem><para>Specify COSE algorithm used in credential generation. The default value is
@ -461,7 +486,15 @@
<xi:include href="version-info.xml" xpointer="v249"/></listitem> <xi:include href="version-info.xml" xpointer="v249"/></listitem>
</varlistentry> </varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>TPM2 Enrollment</title>
<para>The following options are understood that may be used to enroll TPM2 devices:</para>
<variablelist>
<varlistentry> <varlistentry>
<term><option>--tpm2-device=<replaceable>PATH</replaceable></option></term> <term><option>--tpm2-device=<replaceable>PATH</replaceable></option></term>
@ -636,7 +669,15 @@
<xi:include href="version-info.xml" xpointer="v255"/></listitem> <xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry> </varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Other Options</title>
<para>The following additional options are understood:</para>
<variablelist>
<varlistentry> <varlistentry>
<term><option>--wipe-slot=<replaceable>SLOT<optional>,SLOT...</optional></replaceable></option></term> <term><option>--wipe-slot=<replaceable>SLOT<optional>,SLOT...</optional></replaceable></option></term>

View File

@ -577,6 +577,17 @@
<xi:include href="version-info.xml" xpointer="v251"/> <xi:include href="version-info.xml" xpointer="v251"/>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--include-parents</option></term>
<listitem>
<para>Trigger parent devices of found devices even if the parents
won't match the filter condition.
This is useful if we are interested to limit the coldplug activities to
some devices or subsystems.</para>
<xi:include href="version-info.xml" xpointer="v258"/>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>-w</option></term> <term><option>-w</option></term>
<term><option>--settle</option></term> <term><option>--settle</option></term>

View File

@ -38,19 +38,12 @@ __get_tpm2_devices() {
done done
} }
__get_block_devices() {
local i
for i in /dev/*; do
[ -b "$i" ] && printf '%s\n' "$i"
done
}
_systemd_cryptenroll() { _systemd_cryptenroll() {
local comps local comps
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local -A OPTS=( local -A OPTS=(
[STANDALONE]='-h --help --version [STANDALONE]='-h --help --version
--password --recovery-key' --password --recovery-key --list-devices'
[ARG]='--unlock-key-file [ARG]='--unlock-key-file
--unlock-fido2-device --unlock-fido2-device
--unlock-tpm2-device --unlock-tpm2-device
@ -116,7 +109,7 @@ _systemd_cryptenroll() {
return 0 return 0
fi fi
comps=$(__get_block_devices) comps=$(systemd-cryptenroll --list-devices)
COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
return 0 return 0
} }

View File

@ -58,7 +58,7 @@ _udevadm() {
--json --subsystem-match --subsystem-nomatch --attr-match --attr-nomatch --property-match --json --subsystem-match --subsystem-nomatch --attr-match --attr-nomatch --property-match
--tag-match --sysname-match --name-match --parent-match' --tag-match --sysname-match --name-match --parent-match'
[TRIGGER_STANDALONE]='-v --verbose -n --dry-run -q --quiet -w --settle --wait-daemon --uuid [TRIGGER_STANDALONE]='-v --verbose -n --dry-run -q --quiet -w --settle --wait-daemon --uuid
--initialized-match --initialized-nomatch' --initialized-match --initialized-nomatch --include-parents'
[TRIGGER_ARG]='-t --type -c --action -s --subsystem-match -S --subsystem-nomatch [TRIGGER_ARG]='-t --type -c --action -s --subsystem-match -S --subsystem-nomatch
-a --attr-match -A --attr-nomatch -p --property-match -a --attr-match -A --attr-nomatch -p --property-match
-g --tag-match -y --sysname-match --name-match -b --parent-match -g --tag-match -y --sysname-match --name-match -b --parent-match

View File

@ -38,6 +38,7 @@ _udevadm_trigger(){
'--tag-match=[Trigger events for devices with a matching tag.]:TAG' \ '--tag-match=[Trigger events for devices with a matching tag.]:TAG' \
'--sysname-match=[Trigger events for devices with a matching sys device name.]:NAME' \ '--sysname-match=[Trigger events for devices with a matching sys device name.]:NAME' \
'--parent-match=[Trigger events for all children of a given device.]:NAME' \ '--parent-match=[Trigger events for all children of a given device.]:NAME' \
'--include-parents[Also trigger parent devices of found devices.]' \
'--initialized-match[Trigger events for devices that are already initialized.]' \ '--initialized-match[Trigger events for devices that are already initialized.]' \
'--initialized-nomatch[Trigger events for devices that are not initialized yet.]' \ '--initialized-nomatch[Trigger events for devices that are not initialized yet.]' \
'--uuid[Print synthetic uevent UUID.]' \ '--uuid[Print synthetic uevent UUID.]' \

View File

@ -193,7 +193,7 @@ static int help(void) {
"\n%3$sSimple Enrollment:%4$s\n" "\n%3$sSimple Enrollment:%4$s\n"
" --password Enroll a user-supplied password\n" " --password Enroll a user-supplied password\n"
" --recovery-key Enroll a recovery key\n" " --recovery-key Enroll a recovery key\n"
"\n%3$sPKCS11 Enrollment:%4$s\n" "\n%3$sPKCS#11 Enrollment:%4$s\n"
" --pkcs11-token-uri=URI\n" " --pkcs11-token-uri=URI\n"
" Specify PKCS#11 security token URI\n" " Specify PKCS#11 security token URI\n"
"\n%3$sFIDO2 Enrollment:%4$s\n" "\n%3$sFIDO2 Enrollment:%4$s\n"

View File

@ -1058,3 +1058,8 @@ global:
sd_device_monitor_get_timeout; sd_device_monitor_get_timeout;
sd_device_monitor_receive; sd_device_monitor_receive;
} LIBSYSTEMD_256; } LIBSYSTEMD_256;
LIBSYSTEMD_258 {
global:
sd_device_enumerator_add_all_parents;
} LIBSYSTEMD_257;

View File

@ -24,6 +24,17 @@ typedef enum DeviceEnumerationType {
_DEVICE_ENUMERATION_TYPE_INVALID = -EINVAL, _DEVICE_ENUMERATION_TYPE_INVALID = -EINVAL,
} DeviceEnumerationType; } DeviceEnumerationType;
typedef enum MatchFlag {
MATCH_NONE = 0,
MATCH_BASIC = 1u << 0,
MATCH_SYSNAME = 1u << 1,
MATCH_SUBSYSTEM = 1u << 2,
MATCH_PARENT = 1u << 3,
MATCH_TAG = 1u << 4,
MATCH_ALL = (1u << 5) - 1,
} MatchFlag;
struct sd_device_enumerator { struct sd_device_enumerator {
unsigned n_ref; unsigned n_ref;
@ -46,6 +57,7 @@ struct sd_device_enumerator {
Set *match_tag; Set *match_tag;
Set *match_parent; Set *match_parent;
MatchInitializedType match_initialized; MatchInitializedType match_initialized;
MatchFlag parent_match_flags;
}; };
_public_ int sd_device_enumerator_new(sd_device_enumerator **ret) { _public_ int sd_device_enumerator_new(sd_device_enumerator **ret) {
@ -61,6 +73,7 @@ _public_ int sd_device_enumerator_new(sd_device_enumerator **ret) {
.n_ref = 1, .n_ref = 1,
.type = _DEVICE_ENUMERATION_TYPE_INVALID, .type = _DEVICE_ENUMERATION_TYPE_INVALID,
.match_initialized = MATCH_INITIALIZED_COMPAT, .match_initialized = MATCH_INITIALIZED_COMPAT,
.parent_match_flags = MATCH_ALL,
}; };
*ret = TAKE_PTR(enumerator); *ret = TAKE_PTR(enumerator);
@ -273,6 +286,15 @@ _public_ int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enum
return 1; return 1;
} }
_public_ int sd_device_enumerator_add_all_parents(sd_device_enumerator *enumerator) {
assert_return(enumerator, -EINVAL);
enumerator->parent_match_flags = MATCH_NONE;
enumerator->scan_uptodate = false;
return 1;
}
int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator, MatchInitializedType type) { int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator, MatchInitializedType type) {
assert_return(enumerator, -EINVAL); assert_return(enumerator, -EINVAL);
@ -567,15 +589,6 @@ static bool match_subsystem(sd_device_enumerator *enumerator, const char *subsys
return set_fnmatch(enumerator->match_subsystem, enumerator->nomatch_subsystem, subsystem); return set_fnmatch(enumerator->match_subsystem, enumerator->nomatch_subsystem, subsystem);
} }
typedef enum MatchFlag {
MATCH_SYSNAME = 1u << 0,
MATCH_SUBSYSTEM = 1u << 1,
MATCH_PARENT = 1u << 2,
MATCH_TAG = 1u << 3,
MATCH_ALL = (1u << 4) - 1,
} MatchFlag;
static int test_matches( static int test_matches(
sd_device_enumerator *enumerator, sd_device_enumerator *enumerator,
sd_device *device, sd_device *device,
@ -618,6 +631,7 @@ static int test_matches(
!match_tag(enumerator, device)) !match_tag(enumerator, device))
return false; return false;
if (FLAGS_SET(flags, MATCH_BASIC)) {
r = match_initialized(enumerator, device); r = match_initialized(enumerator, device);
if (r <= 0) if (r <= 0)
return r; return r;
@ -630,6 +644,7 @@ static int test_matches(
if (!device_match_sysattr(device, enumerator->match_sysattr, enumerator->nomatch_sysattr)) if (!device_match_sysattr(device, enumerator->match_sysattr, enumerator->nomatch_sysattr))
return false; return false;
}
return true; return true;
} }
@ -743,7 +758,7 @@ static int enumerator_scan_dir_and_add_devices(
/* Also include all potentially matching parent devices in the enumeration. These are things /* Also include all potentially matching parent devices in the enumeration. These are things
* like root busses e.g. /sys/devices/pci0000:00/ or /sys/devices/pnp0/, which ar not * like root busses e.g. /sys/devices/pci0000:00/ or /sys/devices/pnp0/, which ar not
* linked from /sys/class/ or /sys/bus/, hence pick them up explicitly here. */ * linked from /sys/class/ or /sys/bus/, hence pick them up explicitly here. */
k = enumerator_add_parent_devices(enumerator, device, MATCH_ALL); k = enumerator_add_parent_devices(enumerator, device, enumerator->parent_match_flags);
if (k < 0) if (k < 0)
r = k; r = k;
} }

View File

@ -499,6 +499,41 @@ TEST(sd_device_enumerator_add_match_parent) {
} }
} }
TEST(sd_device_enumerator_add_all_parents) {
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
unsigned devices_count_with_parents = 0;
unsigned devices_count_without_parents = 0;
/* STEP 1: enumerate all block devices without all_parents() */
ASSERT_OK(sd_device_enumerator_new(&e));
ASSERT_OK(sd_device_enumerator_allow_uninitialized(e));
/* filter in only a subsystem */
ASSERT_OK(sd_device_enumerator_add_nomatch_sysname(e, "loop*"));
ASSERT_OK(sd_device_enumerator_add_match_subsystem(e, "block", true));
ASSERT_OK(sd_device_enumerator_add_match_property(e, "DEVTYPE", "partition"));
FOREACH_DEVICE(e, dev) {
ASSERT_TRUE(device_in_subsystem(dev, "block"));
ASSERT_TRUE(device_is_devtype(dev, "partition"));
devices_count_without_parents++;
}
log_debug("found %u devices", devices_count_without_parents);
/* STEP 2: enumerate again with all_parents() */
ASSERT_OK(sd_device_enumerator_add_all_parents(e) >= 0);
unsigned int not_filtered_parent_count = 0;
FOREACH_DEVICE(e, dev) {
if (!device_in_subsystem(dev, "block") || !device_is_devtype(dev,"partition"))
not_filtered_parent_count++;
devices_count_with_parents++;
}
log_debug("found %u devices out of %u that would have been excluded without all_parents()", not_filtered_parent_count,devices_count_with_parents);
ASSERT_EQ(devices_count_with_parents, devices_count_without_parents + not_filtered_parent_count);
}
TEST(sd_device_get_child) { TEST(sd_device_get_child) {
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
int r; int r;

View File

@ -137,6 +137,7 @@ int sd_device_enumerator_add_nomatch_sysname(sd_device_enumerator *enumerator, c
int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag); int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag);
int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent); int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent);
int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator); int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator);
int sd_device_enumerator_add_all_parents(sd_device_enumerator *enumerator);
/* device monitor */ /* device monitor */

View File

@ -266,6 +266,7 @@ static int help(void) {
" -y --sysname-match=NAME Trigger devices with this /sys path\n" " -y --sysname-match=NAME Trigger devices with this /sys path\n"
" --name-match=NAME Trigger devices with this /dev name\n" " --name-match=NAME Trigger devices with this /dev name\n"
" -b --parent-match=NAME Trigger devices with that parent device\n" " -b --parent-match=NAME Trigger devices with that parent device\n"
" --include-parents Trigger parent devices of found devices\n"
" --initialized-match Trigger devices that are already initialized\n" " --initialized-match Trigger devices that are already initialized\n"
" --initialized-nomatch Trigger devices that are not initialized yet\n" " --initialized-nomatch Trigger devices that are not initialized yet\n"
" -w --settle Wait for the triggered events to complete\n" " -w --settle Wait for the triggered events to complete\n"
@ -287,6 +288,7 @@ int trigger_main(int argc, char *argv[], void *userdata) {
ARG_PRIORITIZED_SUBSYSTEM, ARG_PRIORITIZED_SUBSYSTEM,
ARG_INITIALIZED_MATCH, ARG_INITIALIZED_MATCH,
ARG_INITIALIZED_NOMATCH, ARG_INITIALIZED_NOMATCH,
ARG_INCLUDE_PARENTS,
}; };
static const struct option options[] = { static const struct option options[] = {
@ -304,6 +306,7 @@ int trigger_main(int argc, char *argv[], void *userdata) {
{ "sysname-match", required_argument, NULL, 'y' }, { "sysname-match", required_argument, NULL, 'y' },
{ "name-match", required_argument, NULL, ARG_NAME }, { "name-match", required_argument, NULL, ARG_NAME },
{ "parent-match", required_argument, NULL, 'b' }, { "parent-match", required_argument, NULL, 'b' },
{ "include-parents", no_argument, NULL, ARG_INCLUDE_PARENTS },
{ "initialized-match", no_argument, NULL, ARG_INITIALIZED_MATCH }, { "initialized-match", no_argument, NULL, ARG_INITIALIZED_MATCH },
{ "initialized-nomatch", no_argument, NULL, ARG_INITIALIZED_NOMATCH }, { "initialized-nomatch", no_argument, NULL, ARG_INITIALIZED_NOMATCH },
{ "settle", no_argument, NULL, 'w' }, { "settle", no_argument, NULL, 'w' },
@ -428,6 +431,11 @@ int trigger_main(int argc, char *argv[], void *userdata) {
return log_error_errno(r, "Failed to add parent match '%s': %m", optarg); return log_error_errno(r, "Failed to add parent match '%s': %m", optarg);
break; break;
} }
case ARG_INCLUDE_PARENTS:
r = sd_device_enumerator_add_all_parents(e);
if (r < 0)
return log_error_errno(r, "Failed to always include all parents: %m");
break;
case 'w': case 'w':
arg_settle = true; arg_settle = true;
break; break;