Compare commits

...

13 Commits

Author SHA1 Message Date
Daan De Meyer 546827f67c
Merge 8c934d1c4a into 81af8f998e 2024-09-18 11:31:17 +02:00
Daan De Meyer 81af8f998e repart: Support specifying multiple directories to ExcludeFiles= 2024-09-18 10:22:33 +02:00
chenjiayi 4fc8a63f9e systemd: rewatch pids under cgroup v1 when sigchld of processes more than main pid and control pid is captured
If `Delegate` is configured in service, cgroup agent will never send out
any datagram as .control subcgroup is generated. Thus systemd will watch
all processes on the cgroup hierarchy for SIGCHLD to deal with unreliable
cgroup notifications.

In this way, systemd should rewatch all processes when any SIGCHLD is
captured, more than the control pid or main pid.
2024-09-18 10:13:20 +02:00
Jason Yundt dfb3155419 man: document ShowStatus and SetShowStatus()
SetShowStatus() was added in order to fix #11447. Recently, I ran into
the exact same problem that OP was experiencing in #11447. I wasn’t able
to figure out how to deal with the problem until I found #11447, and it
took me a while to find #11447.

This commit takes what I learned from reading #11447 and adds it to the
documentation. Hopefully, this will make it easier for other people who
run into the same problem in the future.
2024-09-18 10:11:55 +02:00
Daan De Meyer fc5037e7d7
Merge pull request #34464 from yuwata/test-space-in-path
test: allow to run tests under directory that contains spaces
2024-09-18 08:50:38 +02:00
Yu Watanabe 13f6ec7ce7 test: quote paths to executables
Fixes #34459.
2024-09-18 09:47:04 +09:00
Yu Watanabe 6e1816ef16 kernel-install: unquote plugin paths in KERNEL_INSTALL_PLUGINS
To support the case that paths to plugins contain spaces.

Prompted by #34459
2024-09-18 09:47:00 +09:00
Yu Watanabe 7ac1ad90d0
Merge pull request #34460 from yuwata/test-86-follow-ups
test: follow-ups for TEST-86
2024-09-18 09:31:17 +09:00
Yu Watanabe d265b8afb7 test: drop unused test.sh for TEST-86-MULTI-PROFILE-UKI
The test cannot run with the bash test runner, as it requires python.
Hence, test.sh is not necessary.

Follow-up for a37640653c.
2024-09-18 04:00:05 +09:00
Yu Watanabe 1aab0a5b10 test: minor coding style fixlets
Follow-up for a37640653c.
2024-09-18 03:50:46 +09:00
Daan De Meyer 8c934d1c4a sysext: Deal with nested mounts properly
Nested mounts should be carried over from host to overlayfs to overlayfs
(and back to host if unmerged). Otherwise you run into hard to debug
issues where merging extensions means you can't unmount those nested mounts
anymore as they are hidden by the overlayfs mount.

To fix this, before unmerging any previous extensions, let's move the nested
mounts from the hierarchy to the workspace, then set up the new hierachy, and
finally, just before moving the hierarchy into place, move the nested mounts
back into place.

Because there might be multiple nested mounts that consists of one or more
mounts stacked on top of each other, we make sure to move all stacked mounts
properly to the overlayfs. The kernel doesn't really provide a nice way to do
this, so we create a stack, pop off each mount onto the stack and then pop from
the stack again to the destination to re-establish the stacked mounts in the same
order in the destination.
2024-09-12 20:51:57 +02:00
Daan De Meyer cce9bb7f25 sysext: Run unmerge in a subprocess
Preparation for later commit where we need a private mount namespace
for unmerge.
2024-09-12 20:32:54 +02:00
Daan De Meyer e0c207b085 mount-util: Make get_submounts() a public function 2024-09-12 12:21:20 +02:00
19 changed files with 394 additions and 124 deletions

View File

@ -593,8 +593,6 @@ node /org/freedesktop/systemd1 {
<!--method GetJobBefore is not documented!-->
<!--method SetShowStatus is not documented!-->
<!--method ListUnitsFiltered is not documented!-->
<!--method ListUnitsByPatterns is not documented!-->
@ -673,8 +671,6 @@ node /org/freedesktop/systemd1 {
<!--property ConfirmSpawn is not documented!-->
<!--property ShowStatus is not documented!-->
<!--property DefaultStandardOutput is not documented!-->
<!--property DefaultStandardError is not documented!-->
@ -1362,6 +1358,24 @@ node /org/freedesktop/systemd1 {
<para><function>ResetFailedUnit()</function> resets the "failed" state of a specific unit.</para>
<para><function>SetShowStatus()</function> configures the display of status messages during bootup and
shutdown. The <varname>mode</varname> parameter can be set to any value that's valid for the
<varname>systemd.show_status</varname> kernel parameter. For more information about
<varname>systemd.show_status</varname>, see
<citerefentry project="man-pages"><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
The <varname>mode</varname> parameter can also be set to an empty string. When <varname>mode</varname>
is set to an empty string, <function>SetShowStatus()</function> will reset
<varname>ShowStatus</varname> back to its original value. You can use
<function>SetShowStatus()</function> create a service that does something like this:
<orderedlist>
<listitem><para>Send a D-Bus message that will turn off status messages.</para></listitem>
<listitem><para>Block until a reply to that message is received.</para></listitem>
<listitem><para>Print multiples lines without being interrupted by status messages.</para></listitem>
<listitem><para>Send a D-Bus message that will reset <varname>ShowStatus</varname> back to its
original value.</para></listitem>
</orderedlist>
</para>
<para><function>ResetFailed()</function> resets the "failed" state of all units.</para>
<para><function>ListUnits()</function> returns an array of all currently loaded units. Note that
@ -1788,6 +1802,12 @@ node /org/freedesktop/systemd1 {
<para><varname>Environment</varname> encodes the environment block passed to all executed services. It
may be altered with bus calls such as <function>SetEnvironment()</function> (see above).</para>
<para><varname>ShowStatus</varname> encodes systemd's current policy for displaying status messages
during bootup and shutdown. Its value can be any valid value for the
<varname>systemd.show_status</varname> kernel parameter (see
<citerefentry project="man-pages"><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>).
It may be altered using <function>SetShowStatus()</function> (see above).</para>
<para><varname>UnitPath</varname> encodes the currently active unit file search path. It is an array of
file system paths encoded as strings.</para>

View File

@ -483,18 +483,18 @@
<term><varname>ExcludeFiles=</varname></term>
<term><varname>ExcludeFilesTarget=</varname></term>
<listitem><para>Takes an absolute file system path referring to a source file or directory on the
host. This setting may be used to exclude files or directories from the host from being copied into
the file system when <varname>CopyFiles=</varname> is used. This option may be used multiple times to
exclude multiple files or directories from host from being copied into the newly formatted file
system.</para>
<listitem><para>Takes one or more absolute paths, separated by whitespace, each referring to a
source file or directory on the host. This setting may be used to exclude files or directories from
the host from being copied into the file system when <varname>CopyFiles=</varname> is used. This
option may be used multiple times to exclude multiple files or directories from host from being
copied into the newly formatted file system.</para>
<para>If the path is a directory and ends with <literal>/</literal>, only the directory's
contents are excluded but not the directory itself. If the path is a directory and does not end with
<literal>/</literal>, both the directory and its contents are excluded.</para>
<para><varname>ExcludeFilesTarget=</varname> is like <varname>ExcludeFiles=</varname> except that
instead of excluding the path on the host from being copied into the partition, we exclude any files
instead of excluding the path on the host from being copied into the partition, it exclude any files
and directories from being copied into the given path in the partition.</para>
<para>When

View File

@ -3001,7 +3001,12 @@ SystemCallErrorNumber=EPERM</programlisting>
<para><option>tty</option> connects standard output to a tty (as configured via <varname>TTYPath=</varname>,
see below). If the TTY is used for output only, the executed process will not become the controlling process of
the terminal, and will not fail or wait for other processes to release the terminal.</para>
the terminal, and will not fail or wait for other processes to release the terminal. Note: if a unit
tries to print multiple lines to a TTY during bootup or shutdown, then there's a chance that those
lines will be broken up by status messages. <function>SetShowStatus()</function> can be used to
prevent this problem. See
<citerefentry project="man-pages"><refentrytitle>org.freedesktop.systemd1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details.</para>
<para><option>journal</option> connects standard output with the journal, which is accessible via
<citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>. Note

View File

@ -568,7 +568,11 @@
<listitem><para>Enables display of status messages on the
console, as controlled via
<varname>systemd.show_status=1</varname> on the kernel command
line.</para></listitem>
line.</para>
<para>You may want to use <function>SetShowStatus()</function> instead of
<constant>SIGRTMIN+20</constant> in order to prevent race conditions. See
<citerefentry project="man-pages"><refentrytitle>org.freedesktop.systemd1</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para></listitem>
</varlistentry>
<varlistentry>
@ -579,7 +583,11 @@
controlled via
<varname>systemd.show_status=0</varname>
on the kernel command
line.</para></listitem>
line.</para>
<para>You may want to use <function>SetShowStatus()</function> instead of
<constant>SIGRTMIN+21</constant> in order to prevent race conditions. See
<citerefentry project="man-pages"><refentrytitle>org.freedesktop.systemd1</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -4169,7 +4169,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
* detect when the cgroup becomes empty. Note that the control process is always
* our child so it's pointless to watch all other processes. */
if (!control_pid_good(s))
if (!s->main_pid_known || s->main_pid_alien)
if (!s->main_pid_known || s->main_pid_alien || unit_cgroup_delegate(u))
(void) unit_enqueue_rewatch_pids(u);
}

View File

@ -404,15 +404,16 @@ static int context_set_path_strv(Context *c, char* const* strv, const char *sour
static int context_set_plugins(Context *c, const char *s, const char *source) {
_cleanup_strv_free_ char **v = NULL;
int r;
assert(c);
if (c->plugins || !s)
return 0;
v = strv_split(s, NULL);
if (!v)
return log_oom();
r = strv_split_full(&v, s, NULL, EXTRACT_UNQUOTE);
if (r < 0)
return log_error_errno(r, "Failed to parse plugin paths from %s: %m", source);
return context_set_path_strv(c, v, source, "plugins", &c->plugins);
}

View File

@ -46,7 +46,13 @@ echo 'DTBDTBDTBDTB' >"$D/sources/subdir/whatever.dtb"
export KERNEL_INSTALL_CONF_ROOT="$D/sources"
# We "install" multiple plugins, but control which ones will be active via install.conf.
export KERNEL_INSTALL_PLUGINS="${ukify_install} ${loaderentry_install} ${uki_copy_install}"
KERNEL_INSTALL_PLUGINS="'${loaderentry_install}' '${uki_copy_install}'"
if [[ -n "$ukify_install" ]]; then
# shellcheck disable=SC2089
KERNEL_INSTALL_PLUGINS="'${ukify_install}' $KERNEL_INSTALL_PLUGINS"
fi
# shellcheck disable=SC2090
export KERNEL_INSTALL_PLUGINS
export BOOT_ROOT="$D/boot"
export BOOT_MNT="$D/boot"
export MACHINE_ID='3e0484f3634a418b8e6a39e8828b03e3'

View File

@ -1742,8 +1742,9 @@ static int config_parse_exclude_files(
const char *rvalue,
void *data,
void *userdata) {
_cleanup_free_ char *resolved = NULL;
char ***exclude_files = ASSERT_PTR(data);
const char *p = ASSERT_PTR(rvalue);
int r;
if (isempty(rvalue)) {
@ -1751,10 +1752,23 @@ static int config_parse_exclude_files(
return 0;
}
r = specifier_printf(rvalue, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved);
for (;;) {
_cleanup_free_ char *word = NULL, *resolved = NULL;
r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", p);
return 0;
}
if (r == 0)
return 0;
r = specifier_printf(word, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to expand specifiers in ExcludeFiles= path, ignoring: %s", rvalue);
"Failed to expand specifiers in %s path, ignoring: %s", lvalue, word);
return 0;
}
@ -1764,6 +1778,7 @@ static int config_parse_exclude_files(
if (strv_consume(exclude_files, TAKE_PTR(resolved)) < 0)
return log_oom();
}
return 0;
}

View File

@ -1462,11 +1462,6 @@ int remount_idmap(
return remount_idmap_fd(p, userns_fd);
}
typedef struct SubMount {
char *path;
int mount_fd;
} SubMount;
static void sub_mount_clear(SubMount *s) {
assert(s);
@ -1474,7 +1469,7 @@ static void sub_mount_clear(SubMount *s) {
s->mount_fd = safe_close(s->mount_fd);
}
static void sub_mount_array_free(SubMount *s, size_t n) {
void sub_mount_array_free(SubMount *s, size_t n) {
assert(s || n == 0);
for (size_t i = 0; i < n; i++)
@ -1503,10 +1498,7 @@ static void sub_mount_drop(SubMount *s, size_t n) {
}
}
static int get_sub_mounts(
const char *prefix,
SubMount **ret_mounts,
size_t *ret_n_mounts) {
int get_sub_mounts(const char *prefix, SubMount **ret_mounts, size_t *ret_n_mounts) {
_cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL;
_cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL;

View File

@ -12,6 +12,15 @@
#include "macro.h"
#include "pidref.h"
typedef struct SubMount {
char *path;
int mount_fd;
} SubMount;
void sub_mount_array_free(SubMount *s, size_t n);
int get_sub_mounts(const char *prefix, SubMount **ret_mounts, size_t *ret_n_mounts);
int repeat_unmount(const char *path, int flags);
int umount_recursive_full(const char *target, int flags, char **keep);

View File

@ -273,6 +273,106 @@ static int need_reload(
return false;
}
static int move_submounts(const char *src, const char *dst, const char *stack) {
SubMount *submounts = NULL;
size_t n_submounts = 0;
int r = 0;
assert(src);
assert(dst);
assert(stack);
CLEANUP_ARRAY(submounts, n_submounts, sub_mount_array_free);
r = get_sub_mounts(src, &submounts, &n_submounts);
if (r < 0)
return log_error_errno(r, "Failed to get submounts for %s: %m", src);
if (n_submounts == 0)
return 0;
_cleanup_free_ char *dir_stack = path_join(stack, "dir");
if (!dir_stack)
return log_oom();
r = mkdir_p(dir_stack, 0700);
if (r < 0)
return log_error_errno(r, "Failed to create directory %s: %m", dir_stack);
_cleanup_free_ char *file_stack = path_join(stack, "file");
if (!file_stack)
return log_oom();
r = mkdir_parents(file_stack, 0700);
if (r < 0)
return log_error_errno(r, "Failed to create parent directories of %s: %m", file_stack);
r = touch(file_stack);
if (r < 0)
return log_error_errno(r, "Failed to create %s: %m", file_stack);
FOREACH_ARRAY(m, submounts, n_submounts) {
_cleanup_free_ char *t = NULL;
const char *suffix;
struct stat st;
assert_se(suffix = path_startswith(m->path, src));
t = path_join(dst, suffix);
if (!t)
return -ENOMEM;
if (fstat(m->mount_fd, &st) < 0)
return log_error_errno(errno, "Failed to stat %s: %m", m->path);
r = mkdir_parents(t, 0755);
if (r < 0)
return log_error_errno(r, "Failed to create parent directories of %s: %m", t);
if (S_ISDIR(st.st_mode)) {
if (mkdir(t, 0755) < 0 && errno != EEXIST)
return log_error_errno(errno, "Failed to create directory %s: %m", t);
} else {
r = touch(t);
if (r < 0)
return log_error_errno(r, "Failed to create file %s: %m", t);
}
/* Some submounts might be a stack of multiple mounts on top of the same path. We have to
* make sure we move all of them to the destination. Since the kernel does not provide us
* with an interface to move a stack mounts, we do it manually by popping the stack one by
* one onto a temporary location (where we'll get the stack in reverse order), before popping
* it again one by one to the desired location (which will be in the original order again).
* Because we don't know upfront whether a mount will be stacked or not, we don't try to
* optimize this for non-stacked mounts.
*/
stack = S_ISDIR(st.st_mode) ? dir_stack : file_stack;
while (path_is_mount_point(m->path)) {
r = mount_follow_verbose(LOG_DEBUG, m->path, stack, NULL, MS_BIND|MS_REC, NULL);
if (r < 0)
return log_error_errno(r, "Failed to mount %s to %s: %m", m->path, stack);
r = umount_verbose(LOG_DEBUG, m->path, MNT_DETACH);
if (r < 0)
return log_error_errno(r, "Failed to unmount %s", m->path);
}
while (path_is_mount_point(stack)) {
r = mount_follow_verbose(LOG_DEBUG, stack, t, NULL, MS_BIND|MS_REC, NULL);
if (r < 0)
return log_error_errno(r, "Failed to mount %s to %s: %m", stack, t);
r = umount_verbose(LOG_DEBUG, stack, MNT_DETACH);
if (r < 0)
return log_error_errno(r, "Failed to unmount %s", stack);
}
}
return r;
}
static int daemon_reload(void) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
@ -286,9 +386,12 @@ static int daemon_reload(void) {
static int unmerge_hierarchy(
ImageClass image_class,
const char *p) {
const char *p,
const char *submounts_path,
const char *stack_path) {
_cleanup_free_ char *dot_dir = NULL, *work_dir_info_file = NULL;
int n_unmerged = 0;
int r;
assert(p);
@ -338,6 +441,12 @@ static int unmerge_hierarchy(
return log_error_errno(r, "Failed to unmount '%s': %m", dot_dir);
}
/* After we've unmounted the metadata directory, save all other submounts so we can restore
* them after unmerging the hierarchy. */
r = move_submounts(p, submounts_path, stack_path);
if (r < 0)
return r;
r = umount_verbose(LOG_ERR, p, MNT_DETACH|UMOUNT_NOFOLLOW);
if (r < 0)
return r;
@ -349,9 +458,68 @@ static int unmerge_hierarchy(
}
log_info("Unmerged '%s'.", p);
n_unmerged++;
}
return 0;
return n_unmerged;
}
static int unmerge_subprocess(
ImageClass image_class,
char **hierarchies,
const char *workspace) {
int r, ret = 0;
/* Mark the whole of /run as MS_SLAVE, so that we can mount stuff below it that doesn't show up on
* the host otherwise. */
r = mount_nofollow_verbose(LOG_ERR, NULL, "/run", NULL, MS_SLAVE|MS_REC, NULL);
if (r < 0)
return log_error_errno(r, "Failed to remount /run/ MS_SLAVE: %m");
/* Let's create the workspace if it's missing */
r = mkdir_p(workspace, 0700);
if (r < 0)
return log_error_errno(r, "Failed to create '%s': %m", workspace);
_cleanup_free_ char *stack_path = path_join(workspace, "submounts/stack");
if (!stack_path)
return log_oom();
STRV_FOREACH(h, hierarchies) {
_cleanup_free_ char *submounts_path = NULL, *resolved = NULL;
submounts_path = path_join(workspace, "submounts", *h);
if (!submounts_path)
return log_oom();
r = chase(*h, arg_root, CHASE_PREFIX_ROOT, &resolved, NULL);
if (r == -ENOENT) {
log_debug_errno(r, "Hierarchy '%s%s' does not exist, ignoring.", strempty(arg_root), *h);
continue;
}
if (r < 0) {
RET_GATHER(ret, log_error_errno(r, "Failed to resolve path to hierarchy '%s%s': %m", strempty(arg_root), *h));
continue;
}
r = unmerge_hierarchy(image_class, resolved, submounts_path, stack_path);
if (r < 0) {
RET_GATHER(ret, r);
continue;
}
if (r == 0)
continue;
/* If we unmerged something, then we have to move the submounts from the hierarchy back into
* place in the host's original hierarchy. */
r = move_submounts(submounts_path, resolved, stack_path);
if (r < 0)
return r;
}
return ret;
}
static int unmerge(
@ -359,34 +527,34 @@ static int unmerge(
char **hierarchies,
bool no_reload) {
int r, ret = 0;
bool need_to_reload;
pid_t pid;
int r;
r = need_reload(image_class, hierarchies, no_reload);
if (r < 0)
return r;
need_to_reload = r > 0;
STRV_FOREACH(p, hierarchies) {
_cleanup_free_ char *resolved = NULL;
r = safe_fork("(sd-unmerge)", FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_NEW_MOUNTNS, &pid);
if (r < 0)
return log_error_errno(r, "Failed to fork off child: %m");
if (r == 0) {
/* Child with its own mount namespace */
r = chase(*p, arg_root, CHASE_PREFIX_ROOT, &resolved, NULL);
if (r == -ENOENT) {
log_debug_errno(r, "Hierarchy '%s%s' does not exist, ignoring.", strempty(arg_root), *p);
continue;
}
if (r < 0) {
log_error_errno(r, "Failed to resolve path to hierarchy '%s%s': %m", strempty(arg_root), *p);
if (ret == 0)
ret = r;
r = unmerge_subprocess(image_class, hierarchies, "/run/systemd/sysext");
continue;
/* Our namespace ceases to exist here, also implicitly detaching all temporary mounts we
* created below /run. Nice! */
_exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
r = unmerge_hierarchy(image_class, resolved);
if (r < 0 && ret == 0)
ret = r;
}
r = wait_for_terminate_and_check("(sd-unmerge)", pid, WAIT_LOG_ABNORMAL);
if (r < 0)
return r;
if (r > 0)
return log_error_errno(SYNTHETIC_ERRNO(EPROTO), "Failed to unmerge hierarchies");
if (need_to_reload) {
r = daemon_reload();
@ -394,7 +562,7 @@ static int unmerge(
return r;
}
return ret;
return 1;
}
static int verb_unmerge(int argc, char **argv, void *userdata) {
@ -1708,23 +1876,43 @@ static int merge_subprocess(
paths[k] = TAKE_PTR(p);
}
/* Set up a directory where we can unstack stack mounts. This is required to move around stacked
* mounts from one path to another as we cannot move the entire stack at once so we have to pop it
* one mount at a time to a temporary location before restacking it in the desired location. */
_cleanup_free_ char *stack_path = path_join(workspace, "submounts/stack");
if (!stack_path)
return log_oom();
/* Let's now unmerge the status quo ante, since to build the new overlayfs we need a reference to the
* underlying fs. */
STRV_FOREACH(h, hierarchies) {
_cleanup_free_ char *resolved = NULL;
_cleanup_free_ char *submounts_path = NULL, *resolved = NULL;
submounts_path = path_join(workspace, "submounts", *h);
if (!submounts_path)
return log_oom();
r = chase(*h, arg_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved, NULL);
if (r < 0)
return log_error_errno(r, "Failed to resolve hierarchy '%s%s': %m", strempty(arg_root), *h);
r = unmerge_hierarchy(image_class, resolved);
r = unmerge_hierarchy(image_class, resolved, submounts_path, stack_path);
if (r < 0)
return r;
if (r > 0)
continue;
/* If we didn't unmerge anything, then we have to move the submounts from the host's
* original hierarchy. */
r = move_submounts(resolved, submounts_path, stack_path);
if (r < 0)
return r;
}
/* Create overlayfs mounts for all hierarchies */
STRV_FOREACH(h, hierarchies) {
_cleanup_free_ char *meta_path = NULL, *overlay_path = NULL, *merge_hierarchy_workspace = NULL;
_cleanup_free_ char *meta_path = NULL, *overlay_path = NULL, *merge_hierarchy_workspace = NULL, *submounts_path = NULL;
meta_path = path_join(workspace, "meta", *h); /* The place where to store metadata about this instance */
if (!meta_path)
@ -1739,6 +1927,10 @@ static int merge_subprocess(
if (!merge_hierarchy_workspace)
return log_oom();
submounts_path = path_join(workspace, "submounts", *h);
if (!submounts_path)
return log_oom();
r = merge_hierarchy(
image_class,
*h,
@ -1750,6 +1942,13 @@ static int merge_subprocess(
merge_hierarchy_workspace);
if (r < 0)
return r;
/* After the new hierarchy is set up, move the submounts from the original hierarchy into
* place. */
r = move_submounts(submounts_path, overlay_path, stack_path);
if (r < 0)
return r;
}
/* And move them all into place. This is where things appear in the host namespace */

View File

@ -197,7 +197,7 @@ _unused_ static void test_compress_stream(const char *compression,
ASSERT_OK(compress(src, dst, -1, &uncompressed_size));
if (cat) {
assert_se(asprintf(&cmd, "%s %s | diff %s -", cat, pattern, srcfile) > 0);
assert_se(asprintf(&cmd, "%s %s | diff '%s' -", cat, pattern, srcfile) > 0);
assert_se(system(cmd) == 0);
}
@ -212,7 +212,7 @@ _unused_ static void test_compress_stream(const char *compression,
r = decompress(dst, dst2, st.st_size);
assert_se(r == 0);
assert_se(asprintf(&cmd2, "diff %s %s", srcfile, pattern2) > 0);
assert_se(asprintf(&cmd2, "diff '%s' %s", srcfile, pattern2) > 0);
assert_se(system(cmd2) == 0);
log_debug("/* test faulty decompression */");

View File

@ -52,7 +52,8 @@ static void test_event_spawn_self(const char *self, const char *arg, bool with_p
log_debug("/* %s(%s, %s) */", __func__, arg, yes_no(with_pidfd));
assert_se(cmd = strjoin(self, " ", arg));
/* 'self' may contain spaces, hence needs to be quoted. */
assert_se(cmd = strjoin("'", self, "' ", arg));
test_event_spawn_core(with_pidfd, cmd, result_buf, BUF_SIZE);

View File

@ -1,10 +0,0 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="Test Multi-Profile UKI Boots"
# shellcheck source=test/test-functions
. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@"

View File

@ -4,32 +4,32 @@ set -e
ANALYZE="${1:-systemd-analyze}"
$ANALYZE compare-versions 1 lt 2
$ANALYZE compare-versions 1 '<' 2
$ANALYZE compare-versions 1 le 2
$ANALYZE compare-versions 1 '<=' 2
$ANALYZE compare-versions 1 ne 2
$ANALYZE compare-versions 1 '!=' 2
( ! $ANALYZE compare-versions 1 ge 2 )
( ! $ANALYZE compare-versions 1 '>=' 2 )
( ! $ANALYZE compare-versions 1 eq 2 )
( ! $ANALYZE compare-versions 1 '==' 2 )
( ! $ANALYZE compare-versions 1 gt 2 )
( ! $ANALYZE compare-versions 1 '>' 2 )
"$ANALYZE" compare-versions 1 lt 2
"$ANALYZE" compare-versions 1 '<' 2
"$ANALYZE" compare-versions 1 le 2
"$ANALYZE" compare-versions 1 '<=' 2
"$ANALYZE" compare-versions 1 ne 2
"$ANALYZE" compare-versions 1 '!=' 2
( ! "$ANALYZE" compare-versions 1 ge 2 )
( ! "$ANALYZE" compare-versions 1 '>=' 2 )
( ! "$ANALYZE" compare-versions 1 eq 2 )
( ! "$ANALYZE" compare-versions 1 '==' 2 )
( ! "$ANALYZE" compare-versions 1 gt 2 )
( ! "$ANALYZE" compare-versions 1 '>' 2 )
test "$($ANALYZE compare-versions 1 2)" = '1 < 2'
test "$($ANALYZE compare-versions 2 2)" = '2 == 2'
test "$($ANALYZE compare-versions 2 1)" = '2 > 1'
test "$($ANALYZE compare-versions '' '')" = "'' == ''"
test "$("$ANALYZE" compare-versions 1 2)" = '1 < 2'
test "$("$ANALYZE" compare-versions 2 2)" = '2 == 2'
test "$("$ANALYZE" compare-versions 2 1)" = '2 > 1'
test "$("$ANALYZE" compare-versions '' '')" = "'' == ''"
set +e
$ANALYZE compare-versions 1 2; ret1=$?
$ANALYZE compare-versions 2 2; ret2=$?
$ANALYZE compare-versions 2 1; ret3=$?
"$ANALYZE" compare-versions 1 2; ret1=$?
"$ANALYZE" compare-versions 2 2; ret2=$?
"$ANALYZE" compare-versions 2 1; ret3=$?
set -e
test $ret1 == 12
test $ret2 == 0
test $ret3 == 11
test "$ret1" == 12
test "$ret2" == 0
test "$ret3" == 11

View File

@ -44,9 +44,9 @@ test_one() (
fi
if [[ "${input##*/}" =~ \.fstab\.input ]]; then
SYSTEMD_LOG_LEVEL=debug SYSTEMD_IN_INITRD="$initrd" SYSTEMD_SYSFS_CHECK=no SYSTEMD_PROC_CMDLINE="fstab=yes root=fstab" SYSTEMD_FSTAB="$input" SYSTEMD_SYSROOT_FSTAB="/dev/null" $generator "$out" "$out" "$out"
SYSTEMD_LOG_LEVEL=debug SYSTEMD_IN_INITRD="$initrd" SYSTEMD_SYSFS_CHECK=no SYSTEMD_PROC_CMDLINE="fstab=yes root=fstab" SYSTEMD_FSTAB="$input" SYSTEMD_SYSROOT_FSTAB="/dev/null" "$generator" "$out" "$out" "$out"
else
SYSTEMD_LOG_LEVEL=debug SYSTEMD_IN_INITRD="$initrd" SYSTEMD_SYSFS_CHECK=no SYSTEMD_PROC_CMDLINE="fstab=no $(cat "$input")" $generator "$out" "$out" "$out"
SYSTEMD_LOG_LEVEL=debug SYSTEMD_IN_INITRD="$initrd" SYSTEMD_SYSFS_CHECK=no SYSTEMD_PROC_CMDLINE="fstab=no $(cat "$input")" "$generator" "$out" "$out" "$out"
fi
# The option x-systemd.growfs creates symlink to system's systemd-growfs@.service in .mount.wants directory.

View File

@ -53,7 +53,7 @@ for f in $(find "$SOURCE"/test-*.input | sort -V); do
echo "*** Running $f"
prepare_testdir "${f%.input}"
cp "$f" "$TESTDIR/usr/lib/sysusers.d/test.conf"
$SYSUSERS --root="$TESTDIR"
"$SYSUSERS" --root="$TESTDIR"
compare "${f%.*}" ""
done
@ -62,7 +62,7 @@ for f in $(find "$SOURCE"/test-*.input | sort -V); do
echo "*** Running $f on stdin"
prepare_testdir "${f%.input}"
touch "$TESTDIR/etc/sysusers.d/test.conf"
$SYSUSERS --root="$TESTDIR" - <"$f"
"$SYSUSERS" --root="$TESTDIR" - <"$f"
compare "${f%.*}" "on stdin"
done
@ -72,9 +72,9 @@ for f in $(find "$SOURCE"/test-*.input | sort -V); do
prepare_testdir "${f%.input}"
touch "$TESTDIR/etc/sysusers.d/test.conf"
# this overrides test.conf which is masked on disk
$SYSUSERS --root="$TESTDIR" --replace=/etc/sysusers.d/test.conf - <"$f"
"$SYSUSERS" --root="$TESTDIR" --replace=/etc/sysusers.d/test.conf - <"$f"
# this should be ignored
$SYSUSERS --root="$TESTDIR" --replace=/usr/lib/sysusers.d/test.conf - <"$SOURCE/test-1.input"
"$SYSUSERS" --root="$TESTDIR" --replace=/usr/lib/sysusers.d/test.conf - <"$SOURCE/test-1.input"
compare "${f%.*}" "on stdin with --replace"
done
@ -84,7 +84,7 @@ echo "*** Testing --inline"
prepare_testdir "$SOURCE/inline"
# copy a random file to make sure it is ignored
cp "$f" "$TESTDIR/etc/sysusers.d/confuse.conf"
$SYSUSERS --root="$TESTDIR" --inline \
"$SYSUSERS" --root="$TESTDIR" --inline \
"u u1 222 - - /bin/zsh" \
"g g1 111"
@ -95,7 +95,7 @@ echo "*** Testing --inline with --replace"
prepare_testdir "$SOURCE/inline"
# copy a random file to make sure it is ignored
cp "$f" "$TESTDIR/etc/sysusers.d/confuse.conf"
$SYSUSERS --root="$TESTDIR" \
"$SYSUSERS" --root="$TESTDIR" \
--inline \
--replace=/etc/sysusers.d/confuse.conf \
"u u1 222 - - /bin/zsh" \
@ -105,7 +105,7 @@ compare "$SOURCE/inline" "(--inline --replace=…)"
echo "*** Testing --inline with no /etc"
rm -rf "${TESTDIR:?}/etc"
$SYSUSERS --root="$TESTDIR" --inline \
"$SYSUSERS" --root="$TESTDIR" --inline \
"u u1 222 - - /bin/zsh" \
"g g1 111"
@ -136,7 +136,7 @@ for f in $(find "$SOURCE"/test-*.input | sort -V); do
echo "*** Running $f (with login.defs)"
prepare_testdir "${f%.input}"
cp "$f" "$TESTDIR/usr/lib/sysusers.d/test.conf"
$SYSUSERS --root="$TESTDIR"
"$SYSUSERS" --root="$TESTDIR"
# shellcheck disable=SC2050
[ @ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES@ = 1 ] && bound=555 || bound=$system_guid_max
@ -152,7 +152,7 @@ for f in $(find "$SOURCE"/test-*.input | sort -V); do
echo "*** Running $f (with login.defs symlinked)"
prepare_testdir "${f%.input}"
cp "$f" "$TESTDIR/usr/lib/sysusers.d/test.conf"
$SYSUSERS --root="$TESTDIR"
"$SYSUSERS" --root="$TESTDIR"
# shellcheck disable=SC2050
[ @ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES@ = 1 ] && bound=555 || bound=$system_guid_max
@ -166,7 +166,7 @@ for f in $(find "$SOURCE"/unhappy-*.input | sort -V); do
echo "*** Running test $f"
prepare_testdir "${f%.input}"
cp "$f" "$TESTDIR/usr/lib/sysusers.d/test.conf"
SYSTEMD_LOG_LEVEL=info $SYSUSERS --root="$TESTDIR" 2>&1 | tail -n1 | sed -r 's/^[^:]+:[^:]+://' >"$TESTDIR/err"
SYSTEMD_LOG_LEVEL=info "$SYSUSERS" --root="$TESTDIR" 2>&1 | tail -n1 | sed -r 's/^[^:]+:[^:]+://' >"$TESTDIR/err"
if ! diff -u "$TESTDIR/err" "${f%.*}.expected-err"; then
echo >&2 "**** Unexpected error output for $f"
cat >&2 "$TESTDIR/err"

View File

@ -3,6 +3,9 @@
set -eux
set -o pipefail
# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh
FAKE_ROOTS_DIR="$(mktemp -d --tmpdir="" fake-roots-XXX)"
FSTYPE=$(stat --file-system --format "%T" /usr)
@ -1008,5 +1011,26 @@ if [[ -w /usr ]]; then
fi
run_sysext_tests "$FAKE_ROOTS_DIR"
install_extension_images
# Test that nested mountpoints are carried over into and back from the sysext overlayfs.
ln -s /tmp/app0.raw /var/lib/extensions/app0.raw
mkdir /tmp/foo
mkdir /tmp/bar
mount --bind /tmp/bar /usr/share
mount --bind /tmp/foo /usr/share
systemd-sysext merge
test -f /usr/lib/systemd/system/some_file
mountpoint /usr/share
touch /tmp/foo/abc
test -f /usr/share/abc
umount /usr/share
test ! -f /usr/share/abc
systemd-sysext unmerge
test ! -f /usr/lib/systemd/system/some_file
mountpoint /usr/share
touch /tmp/bar/stuff
test -f /usr/share/stuff
umount /usr/share
exit 0

View File

@ -17,9 +17,9 @@ if test -f /run/systemd/stub/profile; then
fi
echo "CURRENT MEASUREMENT:"
/usr/lib/systemd/systemd-measure --current
if test -f /run/systemd/tpm2-pcr-signature.json ; then
if test -f /run/systemd/tpm2-pcr-signature.json; then
echo "CURRENT SIGNATURE:"
jq < /run/systemd/tpm2-pcr-signature.json
jq </run/systemd/tpm2-pcr-signature.json
fi
echo "CURRENT EVENT LOG + PCRS:"
@ -45,7 +45,7 @@ TITLE="Profile Two"' --measure-base=/tmp/extended1.efi --cmdline="testprofile2=1
# 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
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
@ -62,12 +62,12 @@ else
if [ "$ID" = "profile0" ]; then
grep -v testprofile /proc/cmdline
echo "default $(basename "$CURRENT_UKI")@profile1" > "$(bootctl -p)/loader/loader.conf"
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"
echo "default $(basename "$CURRENT_UKI")@profile2" >"$(bootctl -p)/loader/loader.conf"
reboot
exit 0
elif [ "$ID" = "profile2" ]; then