1#!/usr/bin/env bash 2# SPDX-License-Identifier: LGPL-2.1-or-later 3# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- 4# ex: ts=8 sw=4 sts=4 et filetype=sh 5set -eux 6set -o pipefail 7 8ARGS=() 9state_directory=/var/lib/private/ 10if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then 11 # If we're running under sanitizers, we need to use a less restrictive 12 # profile, otherwise LSan syscall would get blocked by seccomp 13 ARGS+=(--profile=trusted) 14 # With the trusted profile DynamicUser is disabled, so the storage is not in private/ 15 state_directory=/var/lib/ 16fi 17 18systemd-dissect --no-pager /usr/share/minimal_0.raw | grep -q '✓ portable service' 19systemd-dissect --no-pager /usr/share/minimal_1.raw | grep -q '✓ portable service' 20systemd-dissect --no-pager /usr/share/app0.raw | grep -q '✓ extension for portable service' 21systemd-dissect --no-pager /usr/share/app1.raw | grep -q '✓ extension for portable service' 22 23export SYSTEMD_LOG_LEVEL=debug 24mkdir -p /run/systemd/system/systemd-portabled.service.d/ 25cat <<EOF >/run/systemd/system/systemd-portabled.service.d/override.conf 26[Service] 27Environment=SYSTEMD_LOG_LEVEL=debug 28EOF 29 30portablectl "${ARGS[@]}" attach --now --runtime /usr/share/minimal_0.raw minimal-app0 31 32systemctl is-active minimal-app0.service 33systemctl is-active minimal-app0-foo.service 34systemctl is-active minimal-app0-bar.service && exit 1 35 36portablectl "${ARGS[@]}" reattach --now --runtime /usr/share/minimal_1.raw minimal-app0 37 38systemctl is-active minimal-app0.service 39systemctl is-active minimal-app0-bar.service 40systemctl is-active minimal-app0-foo.service && exit 1 41 42portablectl list | grep -q -F "minimal_1" 43busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' 44 45portablectl detach --now --runtime /usr/share/minimal_1.raw minimal-app0 46 47portablectl list | grep -q -F "No images." 48busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1 49 50# portablectl also works with directory paths rather than images 51 52unsquashfs -dest /tmp/minimal_0 /usr/share/minimal_0.raw 53unsquashfs -dest /tmp/minimal_1 /usr/share/minimal_1.raw 54 55portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/minimal_0 minimal-app0 56 57systemctl is-active minimal-app0.service 58systemctl is-active minimal-app0-foo.service 59systemctl is-active minimal-app0-bar.service && exit 1 60 61portablectl "${ARGS[@]}" reattach --now --enable --runtime /tmp/minimal_1 minimal-app0 62 63systemctl is-active minimal-app0.service 64systemctl is-active minimal-app0-bar.service 65systemctl is-active minimal-app0-foo.service && exit 1 66 67portablectl list | grep -q -F "minimal_1" 68busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' 69 70portablectl detach --now --enable --runtime /tmp/minimal_1 minimal-app0 71 72portablectl list | grep -q -F "No images." 73busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1 74 75portablectl "${ARGS[@]}" attach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_0.raw app0 76 77systemctl is-active app0.service 78status="$(portablectl is-attached --extension app0 minimal_0)" 79[[ "${status}" == "running-runtime" ]] 80 81portablectl "${ARGS[@]}" reattach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_1.raw app0 82 83systemctl is-active app0.service 84status="$(portablectl is-attached --extension app0 minimal_1)" 85[[ "${status}" == "running-runtime" ]] 86 87portablectl detach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_1.raw app0 88 89portablectl "${ARGS[@]}" attach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app1 90 91systemctl is-active app1.service 92status="$(portablectl is-attached --extension app1 minimal_0)" 93[[ "${status}" == "running-runtime" ]] 94 95# Ensure that adding or removing a version to the image doesn't break reattaching 96cp /usr/share/app1.raw /tmp/app1_2.raw 97portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1_2.raw /usr/share/minimal_1.raw app1 98 99systemctl is-active app1.service 100status="$(portablectl is-attached --extension app1_2 minimal_1)" 101[[ "${status}" == "running-runtime" ]] 102 103portablectl "${ARGS[@]}" reattach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_1.raw app1 104 105systemctl is-active app1.service 106status="$(portablectl is-attached --extension app1 minimal_1)" 107[[ "${status}" == "running-runtime" ]] 108 109portablectl detach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_1.raw app1 110 111# Ensure that the combination of read-only images, state directory and dynamic user works, and that 112# state is retained. Check after detaching, as on slow systems (eg: sanitizers) it might take a while 113# after the service is attached before the file appears. 114grep -q -F bar "${state_directory}/app0/foo" 115grep -q -F baz "${state_directory}/app1/foo" 116 117# portablectl also works with directory paths rather than images 118 119mkdir /tmp/rootdir /tmp/app0 /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc 120mount /usr/share/app0.raw /tmp/app0 121mount /usr/share/app1.raw /tmp/app1 122mount /usr/share/minimal_0.raw /tmp/rootdir 123 124# Fix up os-release to drop the valid PORTABLE_SERVICES field (because we are 125# bypassing the sysext logic in portabled here it will otherwise not see the 126# extensions additional valid prefix) 127grep -v "^PORTABLE_PREFIXES=" /tmp/rootdir/etc/os-release > /tmp/os-release-fix/etc/os-release 128 129mount -t overlay overlay -o lowerdir=/tmp/os-release-fix:/tmp/app1:/tmp/rootdir /tmp/overlay 130 131grep . /tmp/overlay/usr/lib/extension-release.d/* 132grep . /tmp/overlay/etc/os-release 133 134portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/overlay app1 135 136systemctl is-active app1.service 137 138portablectl detach --now --runtime overlay app1 139 140umount /tmp/overlay 141 142portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1 143 144systemctl is-active app0.service 145systemctl is-active app1.service 146 147portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/rootdir/usr/lib/os-release 148portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/extension-release.d/extension-release.app0 149portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/extension-release.d/extension-release.app2 150portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/systemd/system/app1.service 151portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/systemd/system/app0.service 152 153portablectl detach --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1 154 155umount /tmp/rootdir 156umount /tmp/app0 157umount /tmp/app1 158 159# Lack of ID field in os-release should be rejected, but it caused a crash in the past instead 160mkdir -p /tmp/emptyroot/usr/lib 161mkdir -p /tmp/emptyext/usr/lib/extension-release.d 162touch /tmp/emptyroot/usr/lib/os-release 163touch /tmp/emptyext/usr/lib/extension-release.d/extension-release.emptyext 164 165# Remote peer disconnected -> portabled crashed 166res="$(! portablectl attach --extension /tmp/emptyext /tmp/emptyroot 2> >(grep "Remote peer disconnected"))" 167test -z "${res}" 168 169echo OK >/testok 170 171exit 0 172