Add varlink interface for sysinstall (#42261)

Allow graphical installers to use system sysinstall. This will be used
by gnome-setup to install GNOME OS and ideally installers of other
distributions talk to the same varlink interface.
This commit is contained in:
Lennart Poettering
2026-07-03 16:04:25 +02:00
committed by GitHub
13 changed files with 1293 additions and 241 deletions

View File

@@ -256,6 +256,7 @@ shared_sources = files(
'varlink-io.systemd.Resolve.Monitor.c',
'varlink-io.systemd.Shutdown.c',
'varlink-io.systemd.StorageProvider.c',
'varlink-io.systemd.SysInstall.c',
'varlink-io.systemd.SysUpdate.c',
'varlink-io.systemd.SysUpdate.Notify.c',
'varlink-io.systemd.Udev.c',

View File

@@ -29,7 +29,7 @@ static SD_VARLINK_DEFINE_ENUM_TYPE(
SD_VARLINK_FIELD_COMMENT("Always create a new partition table, potentially overwriting an existing table"),
SD_VARLINK_DEFINE_ENUM_VALUE(force));
static SD_VARLINK_DEFINE_ENUM_TYPE(
SD_VARLINK_DEFINE_ENUM_TYPE(
BlockDeviceAction,
SD_VARLINK_FIELD_COMMENT("The device is currently present and a candidate. Emitted both during the initial enumeration and for live uevents (any action other than 'remove' is propagated as 'add', including the synthesized transitions when a device becomes empty or read-only and a relevant ignore* input is in effect)."),
SD_VARLINK_DEFINE_ENUM_VALUE(add),
@@ -93,9 +93,9 @@ static SD_VARLINK_DEFINE_METHOD_FULL(
SD_VARLINK_DEFINE_OUTPUT(subsystem, SD_VARLINK_STRING, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_ERROR(NoCandidateDevices);
static SD_VARLINK_DEFINE_ERROR(ConflictingDiskLabelPresent);
static SD_VARLINK_DEFINE_ERROR(
SD_VARLINK_DEFINE_ERROR(NoCandidateDevices);
SD_VARLINK_DEFINE_ERROR(ConflictingDiskLabelPresent);
SD_VARLINK_DEFINE_ERROR(
InsufficientFreeSpace,
SD_VARLINK_FIELD_COMMENT("Minimal size of the disk required for the installation."),
SD_VARLINK_DEFINE_FIELD(minimalSizeBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
@@ -103,7 +103,7 @@ static SD_VARLINK_DEFINE_ERROR(
SD_VARLINK_DEFINE_FIELD(needFreeBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Size of the selected block device."),
SD_VARLINK_DEFINE_FIELD(currentSizeBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_ERROR(
SD_VARLINK_DEFINE_ERROR(
DiskTooSmall,
SD_VARLINK_FIELD_COMMENT("Minimal size of the disk required for the installation."),
SD_VARLINK_DEFINE_FIELD(minimalSizeBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE),

View File

@@ -4,3 +4,9 @@
#include "sd-varlink-idl.h"
extern const sd_varlink_interface vl_interface_io_systemd_Repart;
extern const sd_varlink_symbol vl_type_BlockDeviceAction;
extern const sd_varlink_symbol vl_error_NoCandidateDevices;
extern const sd_varlink_symbol vl_error_ConflictingDiskLabelPresent;
extern const sd_varlink_symbol vl_error_InsufficientFreeSpace;
extern const sd_varlink_symbol vl_error_DiskTooSmall;

View File

@@ -0,0 +1,125 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "sd-varlink-idl.h"
#include "varlink-io.systemd.SysInstall.h"
#include "varlink-io.systemd.Repart.h"
static SD_VARLINK_DEFINE_ENUM_TYPE(
ProgressPhase,
SD_VARLINK_DEFINE_ENUM_VALUE(encrypt_credentials),
SD_VARLINK_DEFINE_ENUM_VALUE(install_partitions),
SD_VARLINK_DEFINE_ENUM_VALUE(mount_partitions),
SD_VARLINK_DEFINE_ENUM_VALUE(install_kernel),
SD_VARLINK_DEFINE_ENUM_VALUE(install_bootloader),
SD_VARLINK_DEFINE_ENUM_VALUE(unmount_partitions));
static SD_VARLINK_DEFINE_ENUM_TYPE(
DeviceFit,
SD_VARLINK_DEFINE_ENUM_VALUE(enough_free_space),
SD_VARLINK_DEFINE_ENUM_VALUE(insufficent_free_space),
SD_VARLINK_DEFINE_ENUM_VALUE(disk_too_small),
SD_VARLINK_DEFINE_ENUM_VALUE(conflicting_disk_label_present));
static SD_VARLINK_DEFINE_STRUCT_TYPE(
Credential,
SD_VARLINK_FIELD_COMMENT("The id of the credential."),
SD_VARLINK_DEFINE_FIELD(id, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("The value of the credential in base64 encoding."),
SD_VARLINK_DEFINE_FIELD(value, SD_VARLINK_STRING, 0));
static SD_VARLINK_DEFINE_METHOD_FULL(
Run,
SD_VARLINK_SUPPORTS_MORE,
SD_VARLINK_FIELD_COMMENT("Full path to the block device node to operate on."),
SD_VARLINK_DEFINE_INPUT(node, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("Path to directory containing definition files."),
SD_VARLINK_DEFINE_INPUT(definitions, SD_VARLINK_STRING, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("If true, fully erase the target block device."),
SD_VARLINK_DEFINE_INPUT(erase, SD_VARLINK_BOOL, 0),
SD_VARLINK_FIELD_COMMENT("If true, EFI variables are modified to register the installed boot loader in the firmware's boot options database."),
SD_VARLINK_DEFINE_INPUT(variables, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The path to a kernel image, if missing the current kernel is dedected and used."),
SD_VARLINK_DEFINE_INPUT(kernelImagePath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("If true, the current locale is copied to target system"),
SD_VARLINK_DEFINE_INPUT(copyLocale, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("If true, the current keymap is copied to target system"),
SD_VARLINK_DEFINE_INPUT(copyKeymap, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("If true, the current timezone is copied to target system"),
SD_VARLINK_DEFINE_INPUT(copyTimezone, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("A list of credentials to be installed to target system."),
SD_VARLINK_DEFINE_INPUT_BY_TYPE(credentials, Credential, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("If used with the 'more' flag, a phase identifier is sent in progress updates."),
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(phase, ProgressPhase, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("If used with the 'more' flag, an object identifier string is sent in progress updates."),
SD_VARLINK_DEFINE_OUTPUT(object, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("If used with the 'more' flag, a progress percentage (specific to the work done for the specified phase+object is sent in progress updates)."),
SD_VARLINK_DEFINE_OUTPUT(progress, SD_VARLINK_INT, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_METHOD_FULL(
ListCandidateDevices,
SD_VARLINK_REQUIRES_MORE,
SD_VARLINK_FIELD_COMMENT("Path to directory containing definition files, used to evaluate the fit of the target OS for each block device."),
SD_VARLINK_DEFINE_INPUT(definitions, SD_VARLINK_STRING, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("If true, keep the call open after the initial enumeration and stream live add/remove notifications as block-subsystem uevents arrive. The end of the initial enumeration is marked by exactly one notification with action='ready' and no other fields. Defaults to false."),
SD_VARLINK_DEFINE_INPUT(subscribe, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Discriminator field. Only set in subscribe mode. 'add' carries the full device record, 'remove' carries only node, 'ready' carries no other fields and is sent once after the initial enumeration."),
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(action, BlockDeviceAction, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The device node path of the block device."),
SD_VARLINK_DEFINE_OUTPUT(node, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("List of symlinks pointing to the device node, if any."),
SD_VARLINK_DEFINE_OUTPUT(symlinks, SD_VARLINK_STRING, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The Linux kernel disk sequence number identifying the medium."),
SD_VARLINK_DEFINE_OUTPUT(diskseq, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The size of the block device in bytes."),
SD_VARLINK_DEFINE_OUTPUT(sizeBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The device vendor string if known."),
SD_VARLINK_DEFINE_OUTPUT(vendor, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The device model string if known."),
SD_VARLINK_DEFINE_OUTPUT(model, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The subsystem the block device belongs to if known."),
SD_VARLINK_DEFINE_OUTPUT(subsystem, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The fit indicating whether the OS would have enough available space on the device."),
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(fit, DeviceFit, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Minimal size of the disk required for the installation."),
SD_VARLINK_DEFINE_OUTPUT(minimalSizeBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Additional free space needed for the installation."),
SD_VARLINK_DEFINE_OUTPUT(needFreeBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Current allocated size of this block device."),
SD_VARLINK_DEFINE_OUTPUT(currentSizeBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE));
SD_VARLINK_DEFINE_INTERFACE(
io_systemd_SysInstall,
"io.systemd.SysInstall",
SD_VARLINK_INTERFACE_COMMENT("API for installing the OS to another block device."),
SD_VARLINK_SYMBOL_COMMENT("Progress phase identifiers. Note that we might add more phases here, and thus identifiers. Frontends can choose to display the phase to the user in some human readable form, or not do that, but if they do it and they receive a notification for a so far unknown phase, they should just ignore it."),
&vl_type_ProgressPhase,
SD_VARLINK_SYMBOL_COMMENT("Description about the fit of an OS on a block device."),
&vl_type_DeviceFit,
SD_VARLINK_SYMBOL_COMMENT("Discriminator on streamed ListCandidateDevices replies in subscribe mode."),
&vl_type_BlockDeviceAction,
SD_VARLINK_SYMBOL_COMMENT("A credential to install to target OS."),
&vl_type_Credential,
SD_VARLINK_SYMBOL_COMMENT("Invoke the actual installation of the OS. If invoked with 'more' enabled will report progress, otherwise will just report completion."),
&vl_method_Run,
SD_VARLINK_SYMBOL_COMMENT("An incompatible disk label present, and not told to erase it."),
&vl_error_ConflictingDiskLabelPresent,
SD_VARLINK_SYMBOL_COMMENT("The target disk has insufficient free space to fit all requested partitions. (But the disk would fit, if emptied.)"),
&vl_error_InsufficientFreeSpace,
SD_VARLINK_SYMBOL_COMMENT("The target disk is too small to fit the installation. (Regardless if emptied or not.)"),
&vl_error_DiskTooSmall,
SD_VARLINK_SYMBOL_COMMENT("Return a list of candidate block devices, i.e. that support partition scanning and other requirements for successful operation."),
&vl_method_ListCandidateDevices,
SD_VARLINK_SYMBOL_COMMENT("Not a single candidate block device could be found."),
&vl_error_NoCandidateDevices);

View File

@@ -0,0 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-varlink-idl.h"
extern const sd_varlink_interface vl_interface_io_systemd_SysInstall;

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*-->
<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
"https://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
<!--
SPDX-License-Identifier: LGPL-2.1-or-later
This file is part of systemd.
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
-->
<policyconfig>
<vendor>The systemd Project</vendor>
<vendor_url>https://systemd.io</vendor_url>
<action id="io.systemd.sysinstall.Run">
<description gettext-domain="systemd">Install OS to disk</description>
<message gettext-domain="systemd">Authentication is required to install an OS to disk.</message>
<defaults>
<allow_any>auth_admin</allow_any>
<allow_inactive>auth_admin</allow_inactive>
<allow_active>auth_admin_keep</allow_active>
</defaults>
</action>
<action id="io.systemd.sysinstall.ListCandidateDevices">
<description gettext-domain="systemd">List candidate devices</description>
<message gettext-domain="systemd">Authentication is required to list device candidates.</message>
<defaults>
<allow_any>auth_admin</allow_any>
<allow_inactive>auth_admin</allow_inactive>
<allow_active>auth_admin_keep</allow_active>
</defaults>
</action>
</policyconfig>

View File

@@ -8,3 +8,8 @@ executables += [
'sources' : files('sysinstall.c'),
},
]
if conf.get('ENABLE_SYSINSTALL') == 1
install_data('io.systemd.sysinstall.policy',
install_dir : polkitpolicydir)
endif

File diff suppressed because it is too large Load Diff

View File

@@ -34,12 +34,15 @@ if systemd-detect-virt -cq; then
exit 0
fi
# shellcheck source=test/units/test-control.sh
. "$(dirname "$0")"/test-control.sh
# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh
WORKDIR="$(mktemp --directory /tmp/test-sysinstall.XXXXXXXXXX)"
LOOPDEV=""
MOUNTED=0
CRED_VALUE="systemd-sysinstall test credential payload"
CRED_VALUE_BASE64=$(echo -n "$CRED_VALUE" | base64 -w0)
cleanup() {
set +e
@@ -53,42 +56,42 @@ cleanup() {
fi
rm -rf "$WORKDIR"
}
trap cleanup EXIT
# 1) Build a small fake "OS source" tree. systemd-sysinstall picks this up via
# the repart.sysinstall.d definitions: CopyFiles= seeds the new root
# partition with these files.
SOURCE_ROOT="$WORKDIR/sourceroot"
mkdir -p "$SOURCE_ROOT/usr/lib" "$SOURCE_ROOT/etc"
create_fake_os_source_tree() {
# 1) Build a small fake "OS source" tree. systemd-sysinstall picks this up via
# the repart.sysinstall.d definitions: CopyFiles= seeds the new root
# partition with these files.
SOURCE_ROOT="$WORKDIR/sourceroot"
mkdir -p "$SOURCE_ROOT/usr/lib" "$SOURCE_ROOT/etc"
cat >"$SOURCE_ROOT/usr/lib/os-release" <<'EOF'
cat >"$SOURCE_ROOT/usr/lib/os-release" <<'EOF'
ID=testos
NAME="Test OS"
PRETTY_NAME="Test OS for systemd-sysinstall"
VERSION_ID=1
EOF
ln -s ../usr/lib/os-release "$SOURCE_ROOT/etc/os-release"
ln -s ../usr/lib/os-release "$SOURCE_ROOT/etc/os-release"
# 2) Build a minimal UKI. bootctl link only requires a valid PE with .osrel and
# the systemd-stub SBAT marker, so the .linux/.initrd contents do not need
# to be a real kernel.
echo "fake-kernel" >"$WORKDIR/vmlinuz"
echo "fake-initrd" >"$WORKDIR/initrd"
# 2) Build a minimal UKI. bootctl link only requires a valid PE with .osrel and
# the systemd-stub SBAT marker, so the .linux/.initrd contents do not need
# to be a real kernel.
echo "fake-kernel" >"$WORKDIR/vmlinuz"
echo "fake-initrd" >"$WORKDIR/initrd"
ukify build \
--linux "$WORKDIR/vmlinuz" \
--initrd "$WORKDIR/initrd" \
--os-release "@$SOURCE_ROOT/usr/lib/os-release" \
--uname "1.2.3-testkernel" \
--cmdline "quiet" \
--output "$WORKDIR/testuki.efi"
ukify build \
--linux "$WORKDIR/vmlinuz" \
--initrd "$WORKDIR/initrd" \
--os-release "@$SOURCE_ROOT/usr/lib/os-release" \
--uname "1.2.3-testkernel" \
--cmdline "quiet" \
--output "$WORKDIR/testuki.efi"
# 3) Build a sysinstall partition definition: a single ESP plus a root
# partition seeded from the fake source tree.
DEFS="$WORKDIR/sysinstall.d"
mkdir -p "$DEFS"
# 3) Build a sysinstall partition definition: a single ESP plus a root
# partition seeded from the fake source tree.
DEFS="$WORKDIR/sysinstall.d"
mkdir -p "$DEFS"
cat >"$DEFS/10-esp.conf" <<EOF
cat >"$DEFS/10-esp.conf" <<EOF
[Partition]
Type=esp
Format=vfat
@@ -96,7 +99,7 @@ SizeMinBytes=64M
SizeMaxBytes=64M
EOF
cat >"$DEFS/20-root.conf" <<EOF
cat >"$DEFS/20-root.conf" <<EOF
[Partition]
Type=root
Format=ext4
@@ -104,80 +107,119 @@ SizeMinBytes=128M
CopyFiles=$SOURCE_ROOT:/
EOF
# 4) Allocate a sparse target file. systemd-sysinstall accepts a regular file
# path here — systemd-repart and the in-process dissect logic transparently
# handle the loop attach during install. We can't pre-attach the empty file
# via systemd-dissect --attach since that requires a valid DDI.
truncate -s 512M "$WORKDIR/target.img"
# 4) Allocate a sparse target file. systemd-sysinstall accepts a regular file
# path here — systemd-repart and the in-process dissect logic transparently
# handle the loop attach during install. We can't pre-attach the empty file
# via systemd-dissect --attach since that requires a valid DDI.
truncate -s 512M "$WORKDIR/target.img"
}
# 5) Run the installer non-interactively against the target image. Also stash a
# literal credential ('marker') so we can verify it ends up next to the UKI
# and is referenced from the boot loader entry.
CRED_VALUE="systemd-sysinstall test credential payload"
systemd-sysinstall \
--welcome=no \
--chrome=no \
--confirm=no \
--summary=no \
--erase=yes \
--variables=no \
--reboot=no \
--mute-console=no \
--copy-locale=no \
--copy-keymap=no \
--copy-timezone=no \
--set-credential="marker:$CRED_VALUE" \
--kernel="$WORKDIR/testuki.efi" \
--definitions="$DEFS" \
"$WORKDIR/target.img"
validate_image() {
# 1) Attach the freshly installed image as a loopback device for inspection.
LOOPDEV="$(systemd-dissect --attach "$WORKDIR/target.img")"
# 6) Attach the freshly installed image as a loopback device for inspection.
LOOPDEV="$(systemd-dissect --attach "$WORKDIR/target.img")"
# Verify the resulting on-disk layout. The disk must now carry a GPT with at
# least an ESP partition.
sfdisk_dump="$(sfdisk --dump "$LOOPDEV")"
assert_in "C12A7328-F81F-11D2-BA4B-00A0C93EC93B" "$sfdisk_dump"
# Verify the resulting on-disk layout. The disk must now carry a GPT with at
# least an ESP partition.
sfdisk_dump="$(sfdisk --dump "$LOOPDEV")"
assert_in "C12A7328-F81F-11D2-BA4B-00A0C93EC93B" "$sfdisk_dump"
# 2) Mount the image read-only and verify the installed artifacts: an entry
# file referencing the UKI on the ESP, the UKI itself, and the systemd-boot
# binary.
MNT="$WORKDIR/mnt"
mkdir -p "$MNT"
# 7) Mount the image read-only and verify the installed artifacts: an entry
# file referencing the UKI on the ESP, the UKI itself, and the systemd-boot
# binary.
MNT="$WORKDIR/mnt"
mkdir -p "$MNT"
systemd-dissect --mount --read-only "$LOOPDEV" "$MNT"
MOUNTED=1
systemd-dissect --mount --read-only "$LOOPDEV" "$MNT"
MOUNTED=1
ESP="$MNT/efi"
test -d "$ESP/loader/entries"
ESP="$MNT/efi"
test -d "$ESP/loader/entries"
# Exactly one entry should have been linked, and it should reference the UKI
# we passed via --kernel=.
ENTRY=$(find "$ESP/loader/entries" -maxdepth 1 -name '*.conf' -type f | head -n1)
test -n "$ENTRY"
grep -E "^uki /[^/]+/testuki\.efi$" "$ENTRY" >/dev/null
# Exactly one entry should have been linked, and it should reference the UKI
# we passed via --kernel=.
ENTRY=$(find "$ESP/loader/entries" -maxdepth 1 -name '*.conf' -type f | head -n1)
test -n "$ENTRY"
grep -E "^uki /[^/]+/testuki\.efi$" "$ENTRY" >/dev/null
# The UKI file referenced in the entry must exist on the ESP.
UKI_PATH=$(awk '/^uki / { print $2 }' "$ENTRY")
test -n "$UKI_PATH"
test -f "$ESP$UKI_PATH"
# The UKI file referenced in the entry must exist on the ESP.
UKI_PATH=$(awk '/^uki / { print $2 }' "$ENTRY")
test -n "$UKI_PATH"
test -f "$ESP$UKI_PATH"
# bootctl install should have placed sd-boot on the ESP.
find "$ESP/EFI/systemd" -type f -iname 'systemd-boot*.efi' | grep . >/dev/null
# bootctl install should have placed sd-boot on the ESP.
find "$ESP/EFI/systemd" -type f -iname 'systemd-boot*.efi' | grep . >/dev/null
# The credential we passed via --set-credential= must have been encrypted and
# placed next to the UKI, and must be referenced as 'extra' from the entry.
UKI_DIR="$(dirname "$ESP$UKI_PATH")"
TOKEN_DIR="$(basename "$UKI_DIR")"
test -s "$UKI_DIR/marker.cred"
grep -E "^extra /$TOKEN_DIR/marker\.cred$" "$ENTRY" >/dev/null
# The credential we passed via --set-credential= must have been encrypted and
# placed next to the UKI, and must be referenced as 'extra' from the entry.
UKI_DIR="$(dirname "$ESP$UKI_PATH")"
TOKEN_DIR="$(basename "$UKI_DIR")"
test -s "$UKI_DIR/marker.cred"
grep -E "^extra /$TOKEN_DIR/marker\.cred$" "$ENTRY" >/dev/null
# Locale/keymap/timezone propagation is off, so those .cred files must NOT
# exist on the ESP.
test ! -e "$UKI_DIR/firstboot.locale.cred"
test ! -e "$UKI_DIR/firstboot.keymap.cred"
test ! -e "$UKI_DIR/firstboot.timezone.cred"
# Locale/keymap/timezone propagation is off, so those .cred files must NOT
# exist on the ESP.
test ! -e "$UKI_DIR/firstboot.locale.cred"
test ! -e "$UKI_DIR/firstboot.keymap.cred"
test ! -e "$UKI_DIR/firstboot.timezone.cred"
# 3) The seeded files from the fake source tree must end up in the new root.
test -f "$MNT/usr/lib/os-release"
grep '^ID=testos$' "$MNT/usr/lib/os-release" >/dev/null
}
# 8) The seeded files from the fake source tree must end up in the new root.
test -f "$MNT/usr/lib/os-release"
grep '^ID=testos$' "$MNT/usr/lib/os-release" >/dev/null
testcase_sysinstall_basic() {
WORKDIR="$(mktemp --directory /tmp/test-sysinstall.XXXXXXXXXX)"
LOOPDEV=""
MOUNTED=0
echo "WORKDIR=$WORKDIR"
trap cleanup RETURN
create_fake_os_source_tree
# Run the installer non-interactively against the target image. Also stash a
# literal credential ('marker') so we can verify it ends up next to the UKI
# and is referenced from the boot loader entry.
systemd-sysinstall \
--welcome=no \
--chrome=no \
--confirm=no \
--summary=no \
--erase=yes \
--variables=no \
--reboot=no \
--mute-console=no \
--copy-locale=no \
--copy-keymap=no \
--copy-timezone=no \
--set-credential="marker:$CRED_VALUE" \
--kernel="$WORKDIR/testuki.efi" \
--definitions="$DEFS" \
"$WORKDIR/target.img"
validate_image
cleanup
}
testcase_sysinstall_varlink_basic() {
WORKDIR="$(mktemp --directory /tmp/test-sysinstall.XXXXXXXXXX)"
LOOPDEV=""
MOUNTED=0
echo "WORKDIR=$WORKDIR"
trap cleanup RETURN
create_fake_os_source_tree
# Run the installer via varlink against the target image. Also stash a
# literal credential ('marker') so we can verify it ends up next to the UKI
# and is referenced from the boot loader entry.
varlinkctl call /run/systemd/io.systemd.SysInstall io.systemd.SysInstall.Run "{\"erase\": true, \"variables\": false, \"credentials\" : [{ \"id\" : \"marker\", \"value\" : \"$CRED_VALUE_BASE64\" }], \"kernelImagePath\" : \"$WORKDIR/testuki.efi\", \"node\": \"$WORKDIR/target.img\", \"definitions\" : [\"$DEFS\"] }" --more
validate_image
}
run_testcases

View File

@@ -859,6 +859,15 @@ units = [
'conditions' : ['ENABLE_SYSINSTALL'],
'symlinks' : ['system-install.target.wants/'],
},
{
'file' : 'systemd-sysinstall.socket',
'conditions' : ['ENABLE_SYSINSTALL'],
'symlinks' : ['sockets.target.wants/'],
},
{
'file' : 'systemd-sysinstall@.service',
'conditions' : ['ENABLE_SYSINSTALL'],
},
{
'file' : 'systemd-sysupdate-reboot.service',
'conditions' : ['ENABLE_SYSUPDATE'],

View File

@@ -14,6 +14,8 @@ Wants=systemd-logind.service
After=systemd-logind.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=systemd-sysinstall --variables=yes --reboot=yes --mute-console=yes
StandardOutput=tty
StandardInput=tty

View File

@@ -0,0 +1,27 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=System Install Tool
Documentation=man:systemd-sysinstall(8)
DefaultDependencies=no
Before=sockets.target
Conflicts=shutdown.target
Before=shutdown.target
[Socket]
ListenStream=/run/systemd/io.systemd.SysInstall
Symlinks=/run/varlink/registry/io.systemd.SysInstall
FileDescriptorName=varlink
SocketMode=0666
Accept=yes
MaxConnectionsPerSource=16
XAttrEntryPoint=user.varlink=entrypoint
XAttrListen=user.varlink=listen
XAttrAccept=user.varlink=server

View File

@@ -0,0 +1,20 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=System Install Tool
Documentation=man:systemd-sysinstall(8)
DefaultDependencies=no
Wants=modprobe@loop.service modprobe@dm_mod.service
After=modprobe@loop.service modprobe@dm_mod.service systemd-tpm2-setup-early.service
Conflicts=shutdown.target
Before=shutdown.target
[Service]
ExecStart=systemd-sysinstall