Compare commits

...

22 Commits

Author SHA1 Message Date
Lennart Poettering 36296ae2ad
Merge pull request #17152 from keszybz/make-mountapivfs-default
Make MountAPIVFS=yes default
2020-10-01 11:00:02 +02:00
Franck Bui 8ce3de991b
Merge pull request #17214 from poettering/log-generator-fix
fix logging in generators
2020-10-01 10:51:19 +02:00
Lennart Poettering fabf877705
Merge pull request #17154 from keszybz/crypttab-commas
Allow escaping commas in crypttab
2020-10-01 10:26:24 +02:00
Lennart Poettering 9230f5774a
Merge pull request #17213 from keszybz/man-cleanups
Fix links in man pages
2020-10-01 10:24:44 +02:00
Lennart Poettering 7ea3024b50 bootspec: don't fail with EIO if searching for ESP and finding one without an enveloping partition table
If this happens this should just mean: we couldn't find the ESP.

Fixes: #17122
2020-10-01 10:21:02 +02:00
Lennart Poettering dee29aeb59 generator: use kmsg in system-level generators, journal otherwise
Fixes: #17129.
2020-09-30 16:28:34 +02:00
Lennart Poettering ef9bddb799 log: normalize log target condition check
THis doesn't change the condition's logic at all, but is an attempt to
make things a bit more readable: instead of checking log_target !=
LOG_TARGET_AUTO let's actually list the targets where we want to
consider journal/syslog/kmsg, to make things a bit less confusing. After
all the message here is not to avoid them if LOG_TARGET_AUTO is set, but
to definitely do them in the other cases.
2020-09-30 16:17:12 +02:00
Lennart Poettering 27ffec0831 log: update comment
The logic was changed in bc694c06e6, let's
update the comment accordingly.
2020-09-30 16:06:55 +02:00
Zbigniew Jędrzejewski-Szmek 21556381ff man: correct various links
As usual, linkchecker ftw!
2020-09-30 14:57:21 +02:00
Zbigniew Jędrzejewski-Szmek a9d99f6e3d man: update rules
Fixup for 278fdd064d.
2020-09-30 14:52:48 +02:00
Zbigniew Jędrzejewski-Szmek 550f3ba68d man/html: fix invocation for pages which are *not* symlinks
It seems I tested that redirects work, but I forgot to check that non-redirects
are still fine too ;(
2020-09-30 14:43:12 +02:00
Zbigniew Jędrzejewski-Szmek b12bd993c8 man: describe comma escaping in crypttab(5) 2020-09-25 13:36:34 +02:00
Zbigniew Jędrzejewski-Szmek 7bb553bb98 fstab,crypttab: allow escaping of commas
Fixes #17035. We use "," as the separator between arguments in fstab and crypttab
options field, but the kernel started using "," within arguments. Users will need
to escape those nested commas.
2020-09-25 13:36:34 +02:00
Zbigniew Jędrzejewski-Szmek 48904c8bfd core/execute: escape the separator in exported paths
Our paths shouldn't even contain ":", but let's escape it if one somehow sneaks
in.
2020-09-25 13:36:34 +02:00
Zbigniew Jędrzejewski-Szmek d4d9f034b1 basic/strv: allow escaping the separator in strv_join()
The new parameter is false everywhere except for tests, so no functional change
is expected.
2020-09-25 13:36:34 +02:00
Zbigniew Jędrzejewski-Szmek be36bc1e14 cryptsetup: upgrade log line for option parsing error
If we failed here, we would exit with only a debug message.
2020-09-24 16:36:51 +02:00
Zbigniew Jędrzejewski-Szmek 115a7fb624 cryptsetup-generator: drop unused fstat()
The result stopped being used in 71e4e12584.
2020-09-24 16:36:51 +02:00
Zbigniew Jędrzejewski-Szmek 38ee19c04b nspawn: give better message when invoked as non-root without arguments
When invoked as non-root, we would suggest re-running as root without any
further hint. But this immediately spawns a machine from the local directory,
which can be rather surprising. So let's give a better hint.

(In general, I don't think commandline programs should do "significant" things
when invoked without any arguments. In this regard it would be better if
systemd-nspawn would not spawn a machine from the current directory if called
with no arguments and at least "-D ." would be required.)
2020-09-24 16:36:51 +02:00
Zbigniew Jędrzejewski-Szmek fe79f107ef tree-wide: drop assignments to r when we only need errno
If the whole call is simple and we don't need to look at the return value
apart from the conditional, let's use a form without assignment of the return
value. When the function call is more complicated, it still makes sense to
use a temporary variable.
2020-09-24 16:36:43 +02:00
Zbigniew Jędrzejewski-Szmek d583cf45b6 TEST-50-DISSECT: drop now-unneeded MountAPIVFS=yes and full paths to executables
With the previous changes we can simplify the invocations in the test a bit.
2020-09-24 10:03:18 +02:00
Zbigniew Jędrzejewski-Szmek 6119878480 core: turn on MountAPIVFS=true when RootImage or RootDirectory are specified
Lennart wanted to do this back in
01c33c1eff.
For better or worse, this wasn't done because I thought that turning on MountAPIVFS
is a compat break for RootDirectory and people might be negatively surprised by it.
Without this, search for binaries doesn't work (access_fd() requires /proc).
Let's turn it on, but still allow overriding to "no".

When RootDirectory=/, MountAPIVFS=1 doesn't work. This might be a buglet on its
own, but this patch doesn't change the situation.
2020-09-24 10:03:18 +02:00
Zbigniew Jędrzejewski-Szmek 5e98086d16 core: remember when we set ExecContext.mount_apivfs
No functional change intended so far.
2020-09-24 10:03:18 +02:00
28 changed files with 291 additions and 154 deletions

View File

@ -60,10 +60,10 @@
device or file, or a specification of a block device via
<literal>UUID=</literal> followed by the UUID.</para>
<para>The third field specifies an absolute path to a file to read the encryption key from. Optionally,
<para>The third field specifies an absolute path to a file with the encryption key. Optionally,
the path may be followed by <literal>:</literal> and an fstab device specification (e.g. starting with
<literal>LABEL=</literal> or similar); in which case, the path is relative to the device file system
root. If the field is not present or set to <literal>none</literal> or <literal>-</literal>, a key file
<literal>LABEL=</literal> or similar); in which case the path is taken relative to the device file system
root. If the field is not present or is <literal>none</literal> or <literal>-</literal>, a key file
named after the volume to unlock (i.e. the first column of the line), suffixed with
<filename>.key</filename> is automatically loaded from the <filename>/etc/cryptsetup-keys.d/</filename>
and <filename>/run/cryptsetup-keys.d/</filename> directories, if present. Otherwise, the password has to
@ -78,12 +78,12 @@
<varlistentry>
<term><option>cipher=</option></term>
<listitem><para>Specifies the cipher to use. See
<citerefentry project='die-net'><refentrytitle>cryptsetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>
for possible values and the default value of this option. A
cipher with unpredictable IV values, such as
<literal>aes-cbc-essiv:sha256</literal>, is
recommended.</para></listitem>
<listitem><para>Specifies the cipher to use. See <citerefentry
project='die-net'><refentrytitle>cryptsetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>
for possible values and the default value of this option. A cipher with unpredictable IV values, such
as <literal>aes-cbc-essiv:sha256</literal>, is recommended. Embedded commas in the cipher
specification need to be escaped by preceding them with a backslash, see example below.</para>
</listitem>
</varlistentry>
<varlistentry>
@ -498,15 +498,17 @@
<title>Examples</title>
<example>
<title>/etc/crypttab example</title>
<para>Set up four encrypted block devices. One using LUKS for
normal storage, another one for usage as a swap device and two
TrueCrypt volumes.</para>
<para>Set up four encrypted block devices. One using LUKS for normal storage, another one for usage as
a swap device and two TrueCrypt volumes. For the fourth device, the option string is interpreted as two
options <literal>cipher=xchacha12,aes-adiantum-plain64</literal>,
<literal>keyfile-timeout=10s</literal>.</para>
<programlisting>luks UUID=2505567a-9e27-4efe-a4d5-15ad146c258b
swap /dev/sda7 /dev/urandom swap
truecrypt /dev/sda2 /etc/container_password tcrypt
hidden /mnt/tc_hidden /dev/null tcrypt-hidden,tcrypt-keyfile=/etc/keyfile
external /dev/sda3 keyfile:LABEL=keydev keyfile-timeout=10s</programlisting>
external /dev/sda3 keyfile:LABEL=keydev keyfile-timeout=10s,cipher=xchacha12\,aes-adiantum-plain64
</programlisting>
</example>
<example>

View File

@ -304,7 +304,7 @@
setting. Example: <option>--timezone=Europe/Amsterdam</option> will result in the environment
variable <literal>TZ=:Europe/Amsterdam</literal>. (<literal>:</literal> is used intentionally as part
of the timezone specification, see
<citerefentry><refentrytitle>tzset</refentrytitle><manvolnum>3</manvolnum></citerefentry>.)
<citerefentry project='man-pages'><refentrytitle>tzset</refentrytitle><manvolnum>3</manvolnum></citerefentry>.)
</para></listitem>
</varlistentry>

View File

@ -13,7 +13,7 @@ target="man/$1.html"
ninja -C "@BUILD_ROOT@" "$target"
fullname="@BUILD_ROOT@/$target"
redirect="$(readlink "$fullname" 2>/dev/null)"
redirect="$(test -f "$fullname" && readlink "$fullname" || :)"
if [ -n "$redirect" ]; then
ninja -C "@BUILD_ROOT@" "man/$redirect"

View File

@ -1036,12 +1036,14 @@ manpages = [
''],
['udev_device_has_tag',
'3',
['udev_device_get_devlinks_list_entry',
['udev_device_get_current_tags_list_entry',
'udev_device_get_devlinks_list_entry',
'udev_device_get_properties_list_entry',
'udev_device_get_property_value',
'udev_device_get_sysattr_list_entry',
'udev_device_get_sysattr_value',
'udev_device_get_tags_list_entry',
'udev_device_has_current_tag',
'udev_device_set_sysattr_value'],
''],
['udev_device_new_from_syspath',

View File

@ -70,10 +70,10 @@
<para>OS images may use any kind of Linux-supported file systems. In addition they may make use of LUKS
disk encryption, and contain Verity integrity information. Note that qualifying OS images may be booted
with <citerefentry><refentrytitle>system-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
with <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
<option>--image=</option> switch, and be used as root file system for system service using the
<varname>RootImage=</varname> unit file setting, see
<citerefentry><refentrytitle>system.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
<para>Note that the partition table shown when invoked without command switch (as listed below) does not
necessarily show all partitions included in the image, but just the partitions that are understood and
@ -252,8 +252,8 @@
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>system-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>system.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions Specification</ulink>,
<citerefentry project='man-pages'><refentrytitle>umount</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>fdisk</refentrytitle><manvolnum>8</manvolnum></citerefentry>

View File

@ -153,7 +153,9 @@
case the image has multiple partitions, otherwise partition name <literal>root</literal> is implied.
Options for multiple partitions can be specified in a single line with space separators. Assigning an empty
string removes previous assignments. Duplicated options are ignored. For a list of valid mount options, please
refer to <citerefentry><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
refer to
<citerefentry project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
</para>
<para>Valid partition names follow the <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable
Partitions Specification</ulink>.</para>
@ -1009,7 +1011,7 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
<listitem><para>Sets the CPU scheduling policy for executed processes. Takes one of <option>other</option>,
<option>batch</option>, <option>idle</option>, <option>fifo</option> or <option>rr</option>. See
<citerefentry><refentrytitle>sched_setscheduler</refentrytitle><manvolnum>2</manvolnum></citerefentry> for
<citerefentry project='man-pages'><refentrytitle>sched_setscheduler</refentrytitle><manvolnum>2</manvolnum></citerefentry> for
details.</para></listitem>
</varlistentry>
@ -1019,7 +1021,7 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
<listitem><para>Sets the CPU scheduling priority for executed processes. The available priority range depends
on the selected CPU scheduling policy (see above). For real-time scheduling policies an integer between 1
(lowest priority) and 99 (highest priority) can be used. See
<citerefentry><refentrytitle>sched_setscheduler</refentrytitle><manvolnum>2</manvolnum></citerefentry> for
<citerefentry project='man-pages'><refentrytitle>sched_setscheduler</refentrytitle><manvolnum>2</manvolnum></citerefentry> for
details. </para></listitem>
</varlistentry>
@ -1030,7 +1032,7 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
will be reset when the executed processes call
<citerefentry project='man-pages'><refentrytitle>fork</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
and can hence not leak into child processes. See
<citerefentry><refentrytitle>sched_setscheduler</refentrytitle><manvolnum>2</manvolnum></citerefentry>
<citerefentry project='man-pages'><refentrytitle>sched_setscheduler</refentrytitle><manvolnum>2</manvolnum></citerefentry>
for details. Defaults to false.</para></listitem>
</varlistentry>
@ -1043,7 +1045,7 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
are specified by the lower and upper CPU indices separated by a dash. This option may be specified more than
once, in which case the specified CPU affinity masks are merged. If the empty string is assigned, the mask
is reset, all assignments prior to this will have no effect. See
<citerefentry><refentrytitle>sched_setaffinity</refentrytitle><manvolnum>2</manvolnum></citerefentry> for
<citerefentry project='man-pages'><refentrytitle>sched_setaffinity</refentrytitle><manvolnum>2</manvolnum></citerefentry> for
details.</para></listitem>
</varlistentry>

View File

@ -270,9 +270,8 @@
ideas behind systemd, please refer to the
<ulink url="http://0pointer.de/blog/projects/systemd.html">Original Design Document</ulink>.</para>
<para>Note that some but not all interfaces provided
by systemd are covered by the
<ulink url="Portability and">Interface Portability and Stability Promise</ulink>.</para>
<para>Note that some but not all interfaces provided by systemd are covered by the
<ulink url="https://systemd.io/PORTABILITY_AND_STABILITY/">Interface Portability and Stability Promise</ulink>.</para>
<para>Units may be generated dynamically at boot and system
manager reload time, for example based on other configuration

View File

@ -267,12 +267,21 @@ int log_open(void) {
return 0;
}
if (log_target != LOG_TARGET_AUTO || getpid_cached() == 1 || stderr_is_journal()) {
if (getpid_cached() == 1 ||
stderr_is_journal() ||
IN_SET(log_target,
LOG_TARGET_KMSG,
LOG_TARGET_JOURNAL,
LOG_TARGET_JOURNAL_OR_KMSG,
LOG_TARGET_SYSLOG,
LOG_TARGET_SYSLOG_OR_KMSG)) {
if (!prohibit_ipc &&
IN_SET(log_target, LOG_TARGET_AUTO,
if (!prohibit_ipc) {
if (IN_SET(log_target,
LOG_TARGET_AUTO,
LOG_TARGET_JOURNAL_OR_KMSG,
LOG_TARGET_JOURNAL)) {
r = log_open_journal();
if (r >= 0) {
log_close_syslog();
@ -281,9 +290,10 @@ int log_open(void) {
}
}
if (!prohibit_ipc &&
IN_SET(log_target, LOG_TARGET_SYSLOG_OR_KMSG,
if (IN_SET(log_target,
LOG_TARGET_SYSLOG_OR_KMSG,
LOG_TARGET_SYSLOG)) {
r = log_open_syslog();
if (r >= 0) {
log_close_journal();
@ -291,6 +301,7 @@ int log_open(void) {
return r;
}
}
}
if (IN_SET(log_target, LOG_TARGET_AUTO,
LOG_TARGET_JOURNAL_OR_KMSG,

View File

@ -30,7 +30,7 @@ typedef enum LogTarget{
LOG_TARGET_JOURNAL_OR_KMSG,
LOG_TARGET_SYSLOG,
LOG_TARGET_SYSLOG_OR_KMSG,
LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */
LOG_TARGET_AUTO, /* console if stderr is not journal, JOURNAL_OR_KMSG otherwise */
LOG_TARGET_NULL,
_LOG_TARGET_MAX,
_LOG_TARGET_INVALID = -1

View File

@ -367,7 +367,7 @@ int strv_split_colon_pairs(char ***t, const char *s) {
return (int) n;
}
char *strv_join_prefix(char * const *l, const char *separator, const char *prefix) {
char *strv_join_full(char * const *l, const char *separator, const char *prefix, bool unescape_separators) {
char * const *s;
char *r, *e;
size_t n, k, m;
@ -378,11 +378,17 @@ char *strv_join_prefix(char * const *l, const char *separator, const char *prefi
k = strlen(separator);
m = strlen_ptr(prefix);
if (unescape_separators) /* If there separator is multi-char, we won't know how to escape it. */
assert(k == 1);
n = 0;
STRV_FOREACH(s, l) {
if (s != l)
n += k;
n += m + strlen(*s);
bool needs_escaping = unescape_separators && strchr(*s, separator[0]);
n += m + strlen(*s) * (1 + needs_escaping);
}
r = new(char, n+1);
@ -397,6 +403,15 @@ char *strv_join_prefix(char * const *l, const char *separator, const char *prefi
if (prefix)
e = stpcpy(e, prefix);
bool needs_escaping = unescape_separators && strchr(*s, separator[0]);
if (needs_escaping)
for (size_t i = 0; (*s)[i]; i++) {
if ((*s)[i] == separator[0])
*(e++) = '\\';
*(e++) = (*s)[i];
}
else
e = stpcpy(e, *s);
}

View File

@ -91,9 +91,9 @@ static inline char **strv_split(const char *s, const char *separators) {
* string in the vector is an empty string. */
int strv_split_colon_pairs(char ***t, const char *s);
char *strv_join_prefix(char * const *l, const char *separator, const char *prefix);
char *strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separtor);
static inline char *strv_join(char * const *l, const char *separator) {
return strv_join_prefix(l, separator, NULL);
return strv_join_full(l, separator, NULL, false);
}
char **strv_parse_nulstr(const char *s, size_t l);

View File

@ -53,6 +53,7 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_protect_home, protect_home, Pro
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_protect_system, protect_system, ProtectSystem);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_personality, personality, unsigned long);
static BUS_DEFINE_PROPERTY_GET(property_get_ioprio, "i", ExecContext, exec_context_get_effective_ioprio);
static BUS_DEFINE_PROPERTY_GET(property_get_mount_apivfs, "b", ExecContext, exec_context_get_effective_mount_apivfs);
static BUS_DEFINE_PROPERTY_GET2(property_get_ioprio_class, "i", ExecContext, exec_context_get_effective_ioprio, IOPRIO_PRIO_CLASS);
static BUS_DEFINE_PROPERTY_GET2(property_get_ioprio_priority, "i", ExecContext, exec_context_get_effective_ioprio, IOPRIO_PRIO_DATA);
static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_empty_string, "s", NULL);
@ -1143,7 +1144,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("BindPaths", "a(ssbt)", property_get_bind_paths, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("BindReadOnlyPaths", "a(ssbt)", property_get_bind_paths, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TemporaryFileSystem", "a(ss)", property_get_temporary_filesystems, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("MountAPIVFS", "b", bus_property_get_bool, offsetof(ExecContext, mount_apivfs), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("MountAPIVFS", "b", property_get_mount_apivfs, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("KeyringMode", "s", property_get_exec_keyring_mode, offsetof(ExecContext, keyring_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ProtectProc", "s", property_get_protect_proc, offsetof(ExecContext, protect_proc), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ProcSubset", "s", property_get_proc_subset, offsetof(ExecContext, proc_subset), SD_BUS_VTABLE_PROPERTY_CONST),
@ -1805,9 +1806,6 @@ int bus_exec_context_set_transient_property(
if (streq(name, "ProtectControlGroups"))
return bus_set_transient_bool(u, name, &c->protect_control_groups, message, flags, error);
if (streq(name, "MountAPIVFS"))
return bus_set_transient_bool(u, name, &c->mount_apivfs, message, flags, error);
if (streq(name, "CPUSchedulingResetOnFork"))
return bus_set_transient_bool(u, name, &c->cpu_sched_reset_on_fork, message, flags, error);
@ -2635,6 +2633,20 @@ int bus_exec_context_set_transient_property(
return 1;
} else if (streq(name, "MountAPIVFS")) {
bool b;
r = bus_set_transient_bool(u, name, &b, message, flags, error);
if (r < 0)
return r;
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
c->mount_apivfs = b;
c->mount_apivfs_set = true;
}
return 1;
} else if (streq(name, "WorkingDirectory")) {
const char *s;
bool missing_ok;

View File

@ -1927,7 +1927,7 @@ static int build_environment(
if (!pre)
return -ENOMEM;
joined = strv_join_prefix(c->directories[t].paths, ":", pre);
joined = strv_join_full(c->directories[t].paths, ":", pre, true);
if (!joined)
return -ENOMEM;
@ -2027,7 +2027,7 @@ static bool exec_needs_mount_namespace(
return true;
if (context->root_directory) {
if (context->mount_apivfs)
if (exec_context_get_effective_mount_apivfs(context))
return true;
for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
@ -3147,7 +3147,7 @@ static int apply_mount_namespace(
.protect_kernel_modules = context->protect_kernel_modules,
.protect_kernel_logs = context->protect_kernel_logs,
.protect_hostname = context->protect_hostname,
.mount_apivfs = context->mount_apivfs,
.mount_apivfs = exec_context_get_effective_mount_apivfs(context),
.private_mounts = context->private_mounts,
.protect_home = context->protect_home,
.protect_system = context->protect_system,
@ -5185,7 +5185,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
prefix, yes_no(c->private_users),
prefix, protect_home_to_string(c->protect_home),
prefix, protect_system_to_string(c->protect_system),
prefix, yes_no(c->mount_apivfs),
prefix, yes_no(exec_context_get_effective_mount_apivfs(c)),
prefix, yes_no(c->ignore_sigpipe),
prefix, yes_no(c->memory_deny_write_execute),
prefix, yes_no(c->restrict_realtime),
@ -5650,6 +5650,20 @@ int exec_context_get_effective_ioprio(const ExecContext *c) {
return p;
}
bool exec_context_get_effective_mount_apivfs(const ExecContext *c) {
assert(c);
/* Explicit setting wins */
if (c->mount_apivfs_set)
return c->mount_apivfs;
/* Default to "yes" if root directory or image are specified */
if (c->root_image || !empty_or_root(c->root_directory))
return true;
return false;
}
void exec_context_free_log_extra_fields(ExecContext *c) {
assert(c);

View File

@ -174,6 +174,7 @@ struct ExecContext {
bool nice_set:1;
bool ioprio_set:1;
bool cpu_sched_set:1;
bool mount_apivfs_set:1;
/* This is not exposed to the user but available internally. We need it to make sure that whenever we
* spawn /usr/bin/mount it is run in the same process group as us so that the autofs logic detects
@ -409,6 +410,7 @@ bool exec_context_may_touch_console(const ExecContext *c);
bool exec_context_maintains_privileges(const ExecContext *c);
int exec_context_get_effective_ioprio(const ExecContext *c);
bool exec_context_get_effective_mount_apivfs(const ExecContext *c);
void exec_context_free_log_extra_fields(ExecContext *c);

View File

@ -1349,6 +1349,44 @@ int config_parse_exec_cpu_sched_policy(const char *unit,
return 0;
}
int config_parse_exec_mount_apivfs(const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
ExecContext *c = data;
int k;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
if (isempty(rvalue)) {
c->mount_apivfs_set = false;
c->mount_apivfs = false;
return 0;
}
k = parse_boolean(rvalue);
if (k < 0) {
log_syntax(unit, LOG_WARNING, filename, line, k,
"Failed to parse boolean value, ignoring: %s",
rvalue);
return 0;
}
c->mount_apivfs_set = true;
c->mount_apivfs = k;
return 0;
}
int config_parse_numa_mask(const char *unit,
const char *filename,
unsigned line,

View File

@ -43,6 +43,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_io_priority);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_sched_policy);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_sched_prio);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_affinity);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_mount_apivfs);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_secure_bits);
CONFIG_PARSER_PROTOTYPE(config_parse_root_image_options);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_root_hash);

View File

@ -526,8 +526,7 @@ int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) {
assert(fd >= 0);
assert(s);
r = getpeername(fd, &sa.peer.sa, &salen);
if (r < 0)
if (getpeername(fd, &sa.peer.sa, &salen) < 0)
return log_unit_error_errno(UNIT(s), errno, "getpeername failed: %m");
if (!IN_SET(sa.peer.sa.sa_family, AF_INET, AF_INET6, AF_VSOCK)) {

View File

@ -622,7 +622,6 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
static int add_crypttab_devices(void) {
_cleanup_fclose_ FILE *f = NULL;
unsigned crypttab_line = 0;
struct stat st;
int r;
if (!arg_read_crypttab)
@ -635,11 +634,6 @@ static int add_crypttab_devices(void) {
return 0;
}
if (fstat(fileno(f), &st) < 0) {
log_error_errno(errno, "Failed to stat %s: %m", arg_crypttab);
return 0;
}
for (;;) {
_cleanup_free_ char *line = NULL, *name = NULL, *device = NULL, *keyspec = NULL, *options = NULL, *keyfile = NULL, *keydev = NULL;
crypto_device *d = NULL;

View File

@ -294,9 +294,9 @@ static int parse_options(const char *options) {
_cleanup_free_ char *word = NULL;
int r;
r = extract_first_word(&options, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS);
r = extract_first_word(&options, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS | EXTRACT_UNESCAPE_SEPARATORS);
if (r < 0)
return log_debug_errno(r, "Failed to parse options: %m");
return log_error_errno(r, "Failed to parse options: %m");
if (r == 0)
break;
@ -874,6 +874,9 @@ static int run(int argc, char *argv[]) {
return r;
}
log_debug("%s %s ← %s type=%s cipher=%s", __func__,
argv[2], argv[3], strempty(arg_type), strempty(arg_cipher));
/* A delicious drop of snake oil */
(void) mlockall(MCL_FUTURE);

View File

@ -5134,9 +5134,12 @@ static int run(int argc, char *argv[]) {
if (r <= 0)
goto finish;
r = must_be_root();
if (r < 0)
if (geteuid() != 0) {
r = log_warning_errno(SYNTHETIC_ERRNO(EPERM),
argc >= 2 ? "Need to be root." :
"Need to be root (and some arguments are usually required).\nHint: try --help");
goto finish;
}
r = cant_be_in_netns();
if (r < 0)

View File

@ -842,19 +842,21 @@ static int verify_esp_blkid(
else if (r != 0)
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node);
errno = 0;
r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
if (r != 0)
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system type of \"%s\": %m", node);
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
"No filesystem found on \"%s\": %m", node);
if (!streq(v, "vfat"))
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
"File system \"%s\" is not FAT.", node);
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
if (r != 0)
return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node);
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
"File system \"%s\" is not located on a partitioned block device.", node);
if (!streq(v, "gpt"))
return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),

View File

@ -95,7 +95,19 @@ int fstab_filter_options(const char *opts, const char *names,
if (!ret_filtered) {
for (const char *word = opts;;) {
const char *end = word + strcspn(word, ",");
const char *end = word;
/* Look for an *non-escaped* comma separator. Only commas can be escaped, so "\," is
* the only valid escape sequence, so we can do a very simple test here. */
for (;;) {
size_t n = strcspn(end, ",");
end += n;
if (n > 0 && end[-1] == '\\')
end++;
else
break;
}
NULSTR_FOREACH(name, names) {
if (end < word + strlen(name))
@ -128,9 +140,10 @@ int fstab_filter_options(const char *opts, const char *names,
break;
}
} else {
stor = strv_split(opts, ",");
if (!stor)
return -ENOMEM;
r = strv_split_full(&stor, opts, ",", EXTRACT_DONT_COALESCE_SEPARATORS | EXTRACT_UNESCAPE_SEPARATORS);
if (r < 0)
return r;
strv = memdup(stor, sizeof(char*) * (strv_length(stor) + 1));
if (!strv)
return -ENOMEM;
@ -165,7 +178,7 @@ answer:
if (ret_filtered) {
char *f;
f = strv_join(strv, ",");
f = strv_join_full(strv, ",", NULL, true);
if (!f)
return -ENOMEM;

View File

@ -4,6 +4,7 @@
#include <unistd.h>
#include "alloc-util.h"
#include "cgroup-util.h"
#include "dropin.h"
#include "escape.h"
#include "fd-util.h"
@ -620,6 +621,11 @@ int generator_write_cryptsetup_service_section(
}
void log_setup_generator(void) {
/* Disable talking to syslog/journal (i.e. the two IPC-based loggers) if we run in system context. */
if (cg_pid_get_owner_uid(0, NULL) == -ENXIO /* not running in a per-user slice */)
log_set_prohibit_ipc(true);
log_setup_service();
log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); /* This effectively means: journal for per-user generators, kmsg otherwise */
log_parse_environment();
(void) log_open();
}

View File

@ -118,12 +118,13 @@ static int device_new_from_dev_path(const char *devlink, sd_device **ret_device)
assert(devlink);
r = stat(devlink, &st);
if (r < 0)
return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to stat() %s: %m", devlink);
if (stat(devlink, &st) < 0)
return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno,
"Failed to stat() %s: %m", devlink);
if (!S_ISBLK(st.st_mode))
return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "%s does not point to a block device: %m", devlink);
return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK),
"%s does not point to a block device: %m", devlink);
r = sd_device_new_from_devnum(ret_device, 'b', st.st_rdev);
if (r < 0)

View File

@ -18,7 +18,6 @@ static void do_fstab_filter_options(const char *opts,
const char *name_expected,
const char *value_expected,
const char *filtered_expected) {
int r;
const char *name;
_cleanup_free_ char *value = NULL, *filtered = NULL;
@ -34,7 +33,7 @@ static void do_fstab_filter_options(const char *opts,
/* also test the malloc-less mode */
r = fstab_filter_options(opts, remove, &name, NULL, NULL);
log_info("\"%s\" → %d, \"%s\", expected %d, \"%s\"",
log_info("\"%s\" → %d, \"%s\", expected %d, \"%s\"\n-",
opts, r, name,
r_expected, name_expected);
assert_se(r == r_expected);
@ -54,6 +53,12 @@ static void test_fstab_filter_options(void) {
do_fstab_filter_options("opt,other", "x-opt\0opt\0", 1, "opt", NULL, "other");
do_fstab_filter_options("x-opt,other", "opt\0x-opt\0", 1, "x-opt", NULL, "other");
do_fstab_filter_options("opt=0\\,1,other", "opt\0x-opt\0", 1, "opt", "0,1", "other");
do_fstab_filter_options("opt=0,other,x-opt\\,foobar", "x-opt\0opt\0", 1, "opt", "0", "other,x-opt\\,foobar");
do_fstab_filter_options("opt,other,x-opt\\,part", "opt\0x-opt\0", 1, "opt", NULL, "other,x-opt\\,part");
do_fstab_filter_options("opt,other,part\\,x-opt", "x-opt\0opt\0", 1, "opt", NULL, "other,part\\,x-opt");
do_fstab_filter_options("opt,other\\,\\,\\,opt,x-part", "opt\0x-opt\0", 1, "opt", NULL, "other\\,\\,\\,opt,x-part");
do_fstab_filter_options("opto=0,other", "opt\0x-opt\0", 0, NULL, NULL, NULL);
do_fstab_filter_options("opto,other", "opt\0x-opt\0", 0, NULL, NULL, NULL);
do_fstab_filter_options("x-opto,other", "opt\0x-opt\0", 0, NULL, NULL, NULL);

View File

@ -162,71 +162,84 @@ static void test_strv_find_startswith(void) {
}
static void test_strv_join(void) {
_cleanup_free_ char *p = NULL, *q = NULL, *r = NULL, *s = NULL, *t = NULL, *v = NULL, *w = NULL;
log_info("/* %s */", __func__);
p = strv_join((char **)input_table_multiple, ", ");
_cleanup_free_ char *p = strv_join((char **)input_table_multiple, ", ");
assert_se(p);
assert_se(streq(p, "one, two, three"));
q = strv_join((char **)input_table_multiple, ";");
_cleanup_free_ char *q = strv_join((char **)input_table_multiple, ";");
assert_se(q);
assert_se(streq(q, "one;two;three"));
r = strv_join((char **)input_table_multiple, NULL);
_cleanup_free_ char *r = strv_join((char **)input_table_multiple, NULL);
assert_se(r);
assert_se(streq(r, "one two three"));
s = strv_join((char **)input_table_one, ", ");
_cleanup_free_ char *s = strv_join(STRV_MAKE("1", "2", "3,3"), ",");
assert_se(s);
assert_se(streq(s, "one"));
assert_se(streq(s, "1,2,3,3"));
t = strv_join((char **)input_table_none, ", ");
_cleanup_free_ char *t = strv_join((char **)input_table_one, ", ");
assert_se(t);
assert_se(streq(t, ""));
assert_se(streq(t, "one"));
v = strv_join((char **)input_table_two_empties, ", ");
_cleanup_free_ char *u = strv_join((char **)input_table_none, ", ");
assert_se(u);
assert_se(streq(u, ""));
_cleanup_free_ char *v = strv_join((char **)input_table_two_empties, ", ");
assert_se(v);
assert_se(streq(v, ", "));
w = strv_join((char **)input_table_one_empty, ", ");
_cleanup_free_ char *w = strv_join((char **)input_table_one_empty, ", ");
assert_se(w);
assert_se(streq(w, ""));
}
static void test_strv_join_prefix(void) {
_cleanup_free_ char *p = NULL, *q = NULL, *r = NULL, *s = NULL, *t = NULL, *v = NULL, *w = NULL;
static void test_strv_join_full(void) {
log_info("/* %s */", __func__);
p = strv_join_prefix((char **)input_table_multiple, ", ", "foo");
_cleanup_free_ char *p = strv_join_full((char **)input_table_multiple, ", ", "foo", false);
assert_se(p);
assert_se(streq(p, "fooone, footwo, foothree"));
q = strv_join_prefix((char **)input_table_multiple, ";", "foo");
_cleanup_free_ char *q = strv_join_full((char **)input_table_multiple, ";", "foo", false);
assert_se(q);
assert_se(streq(q, "fooone;footwo;foothree"));
r = strv_join_prefix((char **)input_table_multiple, NULL, "foo");
_cleanup_free_ char *r = strv_join_full(STRV_MAKE("a", "a;b", "a:c"), ";", NULL, true);
assert_se(r);
assert_se(streq(r, "fooone footwo foothree"));
assert_se(streq(r, "a;a\\;b;a:c"));
s = strv_join_prefix((char **)input_table_one, ", ", "foo");
_cleanup_free_ char *s = strv_join_full(STRV_MAKE("a", "a;b", "a;;c", ";", ";x"), ";", NULL, true);
assert_se(s);
assert_se(streq(s, "fooone"));
assert_se(streq(s, "a;a\\;b;a\\;\\;c;\\;;\\;x"));
t = strv_join_prefix((char **)input_table_none, ", ", "foo");
_cleanup_free_ char *t = strv_join_full(STRV_MAKE("a", "a;b", "a:c", ";"), ";", "=", true);
assert_se(t);
assert_se(streq(t, ""));
assert_se(streq(t, "=a;=a\\;b;=a:c;=\\;"));
t = mfree(t);
v = strv_join_prefix((char **)input_table_two_empties, ", ", "foo");
_cleanup_free_ char *u = strv_join_full((char **)input_table_multiple, NULL, "foo", false);
assert_se(u);
assert_se(streq(u, "fooone footwo foothree"));
_cleanup_free_ char *v = strv_join_full((char **)input_table_one, ", ", "foo", false);
assert_se(v);
assert_se(streq(v, "foo, foo"));
assert_se(streq(v, "fooone"));
w = strv_join_prefix((char **)input_table_one_empty, ", ", "foo");
_cleanup_free_ char *w = strv_join_full((char **)input_table_none, ", ", "foo", false);
assert_se(w);
assert_se(streq(w, "foo"));
assert_se(streq(w, ""));
_cleanup_free_ char *x = strv_join_full((char **)input_table_two_empties, ", ", "foo", false);
assert_se(x);
assert_se(streq(x, "foo, foo"));
_cleanup_free_ char *y = strv_join_full((char **)input_table_one_empty, ", ", "foo", false);
assert_se(y);
assert_se(streq(y, "foo"));
}
static void test_strv_unquote(const char *quoted, char **list) {
@ -995,7 +1008,7 @@ int main(int argc, char *argv[]) {
test_strv_find_prefix();
test_strv_find_startswith();
test_strv_join();
test_strv_join_prefix();
test_strv_join_full();
test_strv_unquote(" foo=bar \"waldo\" zzz ", STRV_MAKE("foo=bar", "waldo", "zzz"));
test_strv_unquote("", STRV_MAKE_EMPTY);

View File

@ -110,10 +110,8 @@ static const char* parse_token(const char *current, int32_t *val_out) {
static int override_abs(sd_device *dev, int fd, unsigned evcode, const char *value) {
struct input_absinfo absinfo;
const char *next;
int r;
r = ioctl(fd, EVIOCGABS(evcode), &absinfo);
if (r < 0)
if (ioctl(fd, EVIOCGABS(evcode), &absinfo) < 0)
return log_device_error_errno(dev, errno, "Failed to call EVIOCGABS");
next = parse_token(value, &absinfo.minimum);
@ -122,12 +120,12 @@ static int override_abs(sd_device *dev, int fd, unsigned evcode, const char *val
next = parse_token(next, &absinfo.fuzz);
next = parse_token(next, &absinfo.flat);
if (!next)
return log_device_error_errno(dev, SYNTHETIC_ERRNO(EINVAL), "Failed to parse EV_ABS override '%s'", value);
return log_device_error_errno(dev, SYNTHETIC_ERRNO(EINVAL),
"Failed to parse EV_ABS override '%s'", value);
log_device_debug(dev, "keyboard: %x overridden with %"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32"/%"PRIi32,
evcode, absinfo.minimum, absinfo.maximum, absinfo.resolution, absinfo.fuzz, absinfo.flat);
r = ioctl(fd, EVIOCSABS(evcode), &absinfo);
if (r < 0)
if (ioctl(fd, EVIOCSABS(evcode), &absinfo) < 0)
return log_device_error_errno(dev, errno, "Failed to call EVIOCSABS");
return 0;

View File

@ -60,11 +60,12 @@ fi
umount ${image_dir}/mount
umount ${image_dir}/mount2
systemd-run -t --property RootImage=${image}.raw /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t -p RootImage=${image}.raw cat /usr/lib/os-release | grep -q -F "MARKER=1"
mv ${image}.verity ${image}.fooverity
mv ${image}.roothash ${image}.foohash
systemd-run -t --property RootImage=${image}.raw --property RootHash=${image}.foohash --property RootVerity=${image}.fooverity /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t --property RootImage=${image}.raw --property RootHash=${roothash} --property RootVerity=${image}.fooverity /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t -p RootImage=${image}.raw -p RootHash=${image}.foohash -p RootVerity=${image}.fooverity cat /usr/lib/os-release | grep -q -F "MARKER=1"
# Let's use the long option name just here as a test
systemd-run -t --property RootImage=${image}.raw --property RootHash=${roothash} --property RootVerity=${image}.fooverity cat /usr/lib/os-release | grep -q -F "MARKER=1"
mv ${image}.fooverity ${image}.verity
mv ${image}.foohash ${image}.roothash
@ -128,10 +129,11 @@ cat ${image_dir}/mount/etc/os-release | grep -q -F -f $os_release
cat ${image_dir}/mount/usr/lib/os-release | grep -q -F "MARKER=1"
umount ${image_dir}/mount
systemd-run -t --property RootImage=${image}.gpt --property RootHash=${roothash} /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
# add explicit -p MountAPIVFS=yes once to test the parser
systemd-run -t -p RootImage=${image}.gpt -p RootHash=${roothash} -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t --property RootImage=${image}.raw --property RootImageOptions="root:nosuid,dev home:ro,dev ro,noatime" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "nosuid"
systemd-run -t --property RootImage=${image}.gpt --property RootImageOptions="root:ro,noatime root:ro,dev" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "noatime"
systemd-run -t -p RootImage=${image}.raw -p RootImageOptions="root:nosuid,dev home:ro,dev ro,noatime" mount | grep -F "squashfs" | grep -q -F "nosuid"
systemd-run -t -p RootImage=${image}.gpt -p RootImageOptions="root:ro,noatime root:ro,dev" mount | grep -F "squashfs" | grep -q -F "noatime"
mkdir -p mkdir -p ${image_dir}/result
cat >/run/systemd/system/testservice-50a.service <<EOF
@ -140,7 +142,6 @@ Type=oneshot
ExecStart=bash -c "mount >/run/result/a"
BindPaths=${image_dir}/result:/run/result
TemporaryFileSystem=/run
MountAPIVFS=yes
RootImage=${image}.raw
RootImageOptions=root:ro,noatime home:ro,dev relatime,dev
RootImageOptions=nosuid,dev
@ -155,27 +156,28 @@ Type=oneshot
ExecStart=bash -c "mount >/run/result/b"
BindPaths=${image_dir}/result:/run/result
TemporaryFileSystem=/run
MountAPIVFS=yes
RootImage=${image}.gpt
RootImageOptions=root:ro,noatime,nosuid home:ro,dev nosuid,dev
RootImageOptions=home:ro,dev nosuid,dev,%%foo
# this is the default, but let's specify once to test the parser
MountAPIVFS=yes
EOF
systemctl start testservice-50b.service
grep -F "squashfs" ${image_dir}/result/b | grep -q -F "noatime"
# Check that specifier escape is applied %%foo -> %foo
# Check that specifier escape is applied %%foo %foo
busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/testservice_2d50b_2eservice org.freedesktop.systemd1.Service RootImageOptions | grep -F "nosuid,dev,%foo"
# Now do some checks with MountImages, both by itself, with options and in combination with RootImage, and as single FS or GPT image
systemd-run -t --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2:nosuid,dev" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "nosuid"
systemd-run -t --property MountImages="${image}.gpt:/run/img1:root:nosuid ${image}.raw:/run/img2:home:suid" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "nosuid"
systemd-run -t --property MountImages="${image}.raw:/run/img2\:3" /usr/bin/cat /run/img2:3/usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t --property MountImages="${image}.raw:/run/img2\:3:nosuid" --property MountAPIVFS=yes mount | grep -F "squashfs" | grep -q -F "nosuid"
systemd-run -t --property TemporaryFileSystem=/run --property RootImage=${image}.raw --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t --property TemporaryFileSystem=/run --property RootImage=${image}.raw --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t --property TemporaryFileSystem=/run --property RootImage=${image}.gpt --property RootHash=${roothash} --property MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" /usr/bin/cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2:nosuid,dev" mount | grep -F "squashfs" | grep -q -F "nosuid"
systemd-run -t -p MountImages="${image}.gpt:/run/img1:root:nosuid ${image}.raw:/run/img2:home:suid" mount | grep -F "squashfs" | grep -q -F "nosuid"
systemd-run -t -p MountImages="${image}.raw:/run/img2\:3" cat /run/img2:3/usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t -p MountImages="${image}.raw:/run/img2\:3:nosuid" mount | grep -F "squashfs" | grep -q -F "nosuid"
systemd-run -t -p TemporaryFileSystem=/run -p RootImage=${image}.raw -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t -p TemporaryFileSystem=/run -p RootImage=${image}.raw -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img1/usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t -p TemporaryFileSystem=/run -p RootImage=${image}.gpt -p RootHash=${roothash} -p MountImages="${image}.gpt:/run/img1 ${image}.raw:/run/img2" cat /run/img2/usr/lib/os-release | grep -q -F "MARKER=1"
cat >/run/systemd/system/testservice-50c.service <<EOF
[Service]
MountAPIVFS=yes