Files
systemd/test/units/TEST-29-PORTABLE.directory.sh
T
dongshengyuan 63b30468a5 portablectl: retry inspect with PORTABLE_PREFIXES
When no prefix is specified, portablectl inspect first tries the
prefix derived from the image name. This keeps inspect aligned with
attach behavior.

If that lookup finds no matching units, retry with validated
PORTABLE_PREFIXES read from the image os-release. This makes inspect
work for images whose filename does not match their portable service
prefix.

Keep metadata error handling explicit so request-construction failures
are not logged twice, while sd_bus_call() failures still include the
inspect context.

Add a TEST-29-PORTABLE regression case for a directory image whose
name does not match the portable service prefix.

Fixes #37296.

Signed-off-by: dongshengyuan <dongshengyuan@uniontech.com>
2026-07-14 11:26:40 +02:00

178 lines
8.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
# shellcheck disable=SC2233,SC2235
set -eux
set -o pipefail
# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh
# Arrays cannot be exported, so redefine in each test script
ARGS=()
if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
# If we're running under sanitizers, we need to use a less restrictive
# profile, otherwise LSan syscall would get blocked by seccomp
ARGS+=(--profile=trusted)
fi
unsquashfs -force -no-xattrs -d /tmp/minimal_0 /usr/share/minimal_0.raw
unsquashfs -force -no-xattrs -d /tmp/minimal_1 /usr/share/minimal_1.raw
rm -rf /tmp/mismatched-name
cp -a /tmp/minimal_0 /tmp/mismatched-name
portablectl inspect /tmp/mismatched-name | grep -F "minimal-app0.service" >/dev/null
portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/minimal_0 minimal-app0
systemctl is-active minimal-app0.service
systemctl is-active minimal-app0-foo.service
systemctl is-active minimal-app0-bar.service && exit 1
portablectl "${ARGS[@]}" reattach --now --enable --runtime /tmp/minimal_1 minimal-app0
systemctl is-active minimal-app0.service
systemctl is-active minimal-app0-bar.service
systemctl is-active minimal-app0-foo.service && exit 1
portablectl list | grep -F "minimal_1" >/dev/null
busctl tree org.freedesktop.portable1 --no-pager | grep -F '/org/freedesktop/portable1/image/minimal_5f1' >/dev/null
portablectl detach --now --enable --runtime /tmp/minimal_1 minimal-app0
portablectl list | grep -F "No images." >/dev/null
busctl tree org.freedesktop.portable1 --no-pager | grep -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1 >/dev/null
mkdir /tmp/rootdir /tmp/app0 /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc
mount /tmp/app0.raw /tmp/app0
mount /tmp/app1.raw /tmp/app1
mount /usr/share/minimal_0.raw /tmp/rootdir
# Fix up os-release to drop the valid PORTABLE_SERVICES field (because we are
# bypassing the sysext logic in portabled here it will otherwise not see the
# extensions additional valid prefix)
grep -v "^PORTABLE_PREFIXES=" /tmp/rootdir/etc/os-release >/tmp/os-release-fix/etc/os-release
mount -t overlay overlay -o lowerdir=/tmp/os-release-fix:/tmp/app1:/tmp/rootdir /tmp/overlay
grep . /tmp/overlay/usr/lib/extension-release.d/*
grep . /tmp/overlay/etc/os-release
portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/overlay app1
systemctl is-active app1.service
portablectl detach --now --runtime overlay app1
# Ensure --force works also when symlinking
mkdir -p /run/systemd/system.attached/app1.service.d
cat <<EOF >/run/systemd/system.attached/app1.service
[Unit]
Description=App 1
EOF
cat <<EOF >/run/systemd/system.attached/app1.service.d/10-profile.conf
[Unit]
Description=App 1
EOF
cat <<EOF >/run/systemd/system.attached/app1.service.d/20-portable.conf
[Unit]
Description=App 1
EOF
systemctl daemon-reload
portablectl "${ARGS[@]}" attach --force --copy=symlink --now --runtime /tmp/overlay app1
systemctl is-active app1.service
portablectl detach --now --runtime overlay app1
umount /tmp/overlay
portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
systemctl is-active app0.service
systemctl is-active app1.service
portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -f /tmp/rootdir/usr/lib/os-release >/dev/null
portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -f /tmp/app0/usr/lib/extension-release.d/extension-release.app0 >/dev/null
portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -f /tmp/app1/usr/lib/extension-release.d/extension-release.app2 >/dev/null
portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -f /tmp/app1/usr/lib/systemd/system/app1.service >/dev/null
portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -f /tmp/app0/usr/lib/systemd/system/app0.service >/dev/null
grep -q -F "LogExtraFields=PORTABLE=app0" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_ROOT=rootdir" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_ROOT=rootdir" /run/systemd/system.attached/app1.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0" /run/systemd/system.attached/app1.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app1.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app1.service.d/20-portable.conf
portablectl detach --clean --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
# Ensure --clean remove state and other directories belonging to the portable image being detached
test ! -d /var/lib/app0
test ! -d /run/app0
# Ensure that mixed mode copies the images and units (client-owned) but symlinks the profile (OS owned)
portablectl "${ARGS[@]}" attach --copy=mixed --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
test -d /run/portables/app0
test -d /run/portables/app1
test -d /run/portables/rootdir
test -f /run/systemd/system.attached/app0.service
test -f /run/systemd/system.attached/app1.service
test -L /run/systemd/system.attached/app0.service.d/10-profile.conf
test -L /run/systemd/system.attached/app1.service.d/10-profile.conf
portablectl detach --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
# Ensure that --force works with directory extensions, and that ExtensionDirectories=
# is not decorated with :x-systemd.relax-extension-release-check
portablectl "${ARGS[@]}" attach --force --copy=symlink --now --runtime --extension /tmp/app0 /tmp/rootdir app0
systemctl is-active app0.service
status="$(portablectl is-attached --extension app0 rootdir)"
[[ "${status}" == "running-runtime" ]]
grep -q -F "ExtensionDirectories=" /run/systemd/system.attached/app0.service.d/20-portable.conf
(! grep -q -F "x-systemd.relax-extension-release-check" /run/systemd/system.attached/app0.service.d/20-portable.conf)
portablectl detach --now --runtime --extension /tmp/app0 /tmp/rootdir app0
# Attempt to disable the app unit during detaching. Requires --copy=symlink to reproduce.
# Provides coverage for https://github.com/systemd/systemd/issues/23481
portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0
portablectl detach --now --runtime --enable /tmp/rootdir minimal-app0
# Attach and detach again to check if drop-in-only leftovers are still recognized and removed.
portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0
rm /run/systemd/system.attached/minimal-app0*.service
status="$(portablectl is-attached --runtime /tmp/rootdir)"
[[ "${status}" == "running-runtime" ]]
systemctl stop minimal-app0.service minimal-app0-foo.service
status="$(portablectl is-attached --runtime /tmp/rootdir)"
[[ "${status}" == "attached-runtime" ]]
portablectl detach --now --runtime --enable /tmp/rootdir minimal-app0
[[ ! -d /run/systemd/system.attached/minimal-app0.service.d ]]
[[ ! -d /run/systemd/system.attached/minimal-app0-foo.service.d ]]
# The wrong file should be ignored, given the right one has the xattr set
trap 'rm -rf /var/cache/wrongext' EXIT
mkdir -p /var/cache/wrongext/usr/lib/extension-release.d /var/cache/wrongext/usr/lib/systemd/system/
echo "[Service]" >/var/cache/wrongext/usr/lib/systemd/system/app0.service
touch /var/cache/wrongext/usr/lib/extension-release.d/extension-release.wrongext_somethingwrong.txt
cp /tmp/rootdir/usr/lib/os-release /var/cache/wrongext/usr/lib/extension-release.d/extension-release.app0
setfattr -n user.extension-release.strict -v "false" /var/cache/wrongext/usr/lib/extension-release.d/extension-release.app0
portablectl "${ARGS[@]}" attach --runtime --extension /var/cache/wrongext /tmp/rootdir app0
status="$(portablectl is-attached --extension wrongext rootdir)"
[[ "${status}" == "attached-runtime" ]]
portablectl detach --runtime --extension /var/cache/wrongext /tmp/rootdir app0
umount /tmp/rootdir
umount /tmp/app0
umount /tmp/app1