systemd-sysinstall (this time on top of bootctl link) (#41877)
This is a reiteration of #38764, but this time on top of "bootctl link" for installing a kernel, i.e. #41543, instead of "kernel-install". It's the same as #41543, only adds three new commits on top.
This commit is contained in:
@@ -186,6 +186,17 @@ SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
use as additional search condition. Use case: images that combined a sysext
|
||||
partition with a portable service partition in one.
|
||||
|
||||
- **systemd-sysinstall:**
|
||||
- make systemd-sysinstall itself a varlink service
|
||||
- read installation definition from json file
|
||||
- polkit support in sysinstall
|
||||
- sysinstall: permit driving installer via credentials
|
||||
- add --offline=no mode where we talk to socket based services rather than forking off
|
||||
- if a user doesn't pick a locale during boot into installer, don't ask again after install, because we suppressed credential propagation
|
||||
|
||||
- repart: add MatchLabel= which matches against partition label, so that we
|
||||
truly can install different images in parallel
|
||||
|
||||
- add "systemctl wait" or so, which does what "systemd-run --wait" does, but
|
||||
for all units. It should be both a way to pin units into memory as well as a
|
||||
wait to retrieve their exit data.
|
||||
@@ -1200,14 +1211,6 @@ SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
- introduce a new group to own TPM devices
|
||||
|
||||
- introduce a small "systemd-installer" tool or so, that glues
|
||||
systemd-repart-as-installer and bootctl-install into one. Would just
|
||||
interactively ask user for target disk (with completion and so on), and then do
|
||||
two varlink calls to the the two tools with the right parameters. To support
|
||||
"offline" operation, optionally invoke the two tools directly as child
|
||||
processes with varlink communication over socketpair(). This all should be
|
||||
useful as blueprint for graphical installers which should do the same.
|
||||
|
||||
- introduce an option (or replacement) for "systemctl show" that outputs all
|
||||
properties as JSON, similar to busctl's new JSON output. In contrast to that
|
||||
it should skip the variant type string though.
|
||||
@@ -2491,10 +2494,6 @@ SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
that we can sanely copy ESP contents, /usr/ images, and then set up btrfs
|
||||
raid for the root fs to extend/mirror the existing install. This would be
|
||||
very similar to the concept of live-install-through-btrfs-migration.
|
||||
- add --installer or so, that will interactively ask for a
|
||||
target disk, maybe ask for confirmation, and install something on disk. Then,
|
||||
hook that into installer.target or so, so that it can be used to
|
||||
install/replicate installs
|
||||
- should probably enable btrfs' "temp_fsid" feature for all file
|
||||
systems it creates, as we have no interest in RAID for repart, and it should
|
||||
make sure that we can mount them trivially everywhere.
|
||||
|
||||
@@ -1218,6 +1218,10 @@ manpages = [
|
||||
'systemd-sysext-sysroot.service',
|
||||
'systemd-sysext.service'],
|
||||
'ENABLE_SYSEXT'],
|
||||
['systemd-sysinstall',
|
||||
'8',
|
||||
['systemd-sysinstall.service'],
|
||||
'ENABLE_SYSINSTALL'],
|
||||
['systemd-system-update-generator', '8', [], ''],
|
||||
['systemd-system.conf',
|
||||
'5',
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
<?xml version='1.0'?> <!--*-nxml-*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
||||
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
|
||||
|
||||
<refentry id="systemd-sysinstall" conditional='ENABLE_SYSINSTALL'
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<refentryinfo>
|
||||
<title>systemd-sysinstall</title>
|
||||
<productname>systemd</productname>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>systemd-sysinstall</refentrytitle>
|
||||
<manvolnum>8</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>systemd-sysinstall</refname>
|
||||
<refname>systemd-sysinstall.service</refname>
|
||||
<refpurpose>Simple OS installer</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-sysinstall</command>
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
<arg choice="opt">BLOCKDEVICE</arg>
|
||||
</cmdsynopsis>
|
||||
|
||||
<para><filename>systemd-sysinstall.service</filename></para>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><command>systemd-sysinstall</command> is a simple terminal and command line based operating system
|
||||
installer tool. Its primary use-case is to act as an automatically started interactive interface when
|
||||
booting from an installer medium (e.g. a USB stick), in order to install an OS onto a target
|
||||
disk. However, it may also be invoked directly from a shell. It executes the following steps:</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem><para>It prompts the user for the target disk to install the OS on. (Unless the block device
|
||||
is already specified on the command line.)</para></listitem>
|
||||
|
||||
<listitem><para>It validates whether the disk is suitable (i.e. large enough, and with enough
|
||||
free/unpartitioned space) for an OS installation. If it is generally suitable the user is prompted if they
|
||||
want to erase the disk before installation, or if the OS shall be added to the existing partitions on
|
||||
the disk (the latter only if enough free/unpartitioned disk space is available).</para></listitem>
|
||||
|
||||
<listitem><para>It prompts the user whether to register the newly installed OS with the firmware boot option menu.</para></listitem>
|
||||
|
||||
<listitem><para>It requests confirmation from the user, after showing a summary of the planned OS installation.</para></listitem>
|
||||
|
||||
<listitem><para>It invokes
|
||||
<citerefentry><refentrytitle>systemd-creds</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
|
||||
<command>encrypt</command> command in order to generate encrypted (TPM locked, if available) system
|
||||
credential files for a few, very basic system settings of the currently booted system (locale, keymap,
|
||||
timezone), which it will install on the target disk, parameterizing the invoked kernel. (Or in other
|
||||
words, it prepares that some settings already in effect on the installer system are propagated securely
|
||||
onto the new installation.)</para></listitem>
|
||||
|
||||
<listitem><para>It invokes
|
||||
<citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry> with
|
||||
a definitions directory of <filename>/usr/lib/repart.sysinstall.d/</filename> (only if populated – if
|
||||
not will use the default of <filename>/usr/lib/repart.d/</filename>). This is supposed to set up the
|
||||
basic OS partition structure on the target disk and copies in basic OS partitions (most importantly the
|
||||
<filename>/usr/</filename> hierarchy).</para></listitem>
|
||||
|
||||
<listitem><para>It invokes
|
||||
<citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
|
||||
<command>link</command> command to install an OS kernel image onto the target disk's ESP/XBOOTLDR,
|
||||
together with the credential files prepared earlier.</para></listitem>
|
||||
|
||||
<listitem><para>It invokes
|
||||
<citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
|
||||
<command>install</command> command to install the
|
||||
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> boot
|
||||
loader onto the target disk's ESP.</para></listitem>
|
||||
|
||||
<listitem><para>After confirmation, it reboots the system.</para></listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>Note that the prompts/confirmation may be disabled via the command line, enabling fully automatic,
|
||||
non-interactive installation. See below.</para>
|
||||
|
||||
<para>Note this tool does not interactively query the user for a user to create or a root password to be
|
||||
set on the target system, under the assumption these questions are better prompted from within the newly
|
||||
installed system's first boot process, for example via the
|
||||
<citerefentry><refentrytitle>systemd-firstboot</refentrytitle><manvolnum>1</manvolnum></citerefentry> or
|
||||
<filename>systemd-homed-firstboot.service</filename> components. Note that if required such settings
|
||||
may be propagated explicitly via the <option>--load-credential=</option> switch below.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Options</title>
|
||||
|
||||
<para>The following options are understood:</para>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--definitions=</option></term>
|
||||
|
||||
<listitem><para>Overrides the directory where <command>systemd-repart</command> shall read its
|
||||
partition definitions from, in place of the default of
|
||||
<filename>/usr/lib/repart.sysinstall.d/</filename>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--welcome=</option></term>
|
||||
|
||||
<listitem><para>Takes a boolean argument. Controls whether to show the brief welcome text normally
|
||||
displayed at the beginning of the installation. Defaults to true.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--chrome=</option></term>
|
||||
|
||||
<listitem><para>Takes a boolean argument. Controls whether to show the colored bars at the top and
|
||||
bottom of the terminal interface. Defaults to true.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--erase=</option></term>
|
||||
|
||||
<listitem><para>Takes a boolean argument. Controls whether to erase the current contents of the
|
||||
target disk. If this switch is not used the user is prompted.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--confirm=</option></term>
|
||||
|
||||
<listitem><para>Takes a boolean argument. Controls whether to interactively query the user for
|
||||
confirmation before initiating the OS installation. Defaults to true.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--reboot=</option></term>
|
||||
|
||||
<listitem><para>Takes a boolean argument. Controls whether to reboot the system after completing the
|
||||
installation. Defaults to false.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--variables=</option></term>
|
||||
|
||||
<listitem><para>Takes a boolean argument. Controls whether to register the installed boot loader in
|
||||
the firmware's boot options database. If not specified the user will be prompted.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--summary=</option></term>
|
||||
|
||||
<listitem><para>Takes a boolean argument. Controls whether to show a summary of the choices made
|
||||
before asking for confirmation to proceed with the OS installation. Defaults to true.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--kernel=</option></term>
|
||||
|
||||
<listitem><para>Takes a path to a unified kernel image (UKI). Explicitly selects the kernel image to
|
||||
install on the target disk. If unspecified the currently booted kernel image is installed on the
|
||||
target disk.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--set-credential=<replaceable>id</replaceable>:<replaceable>value</replaceable></option></term>
|
||||
|
||||
<listitem><para>Accepts an additional system credential to encrypt (with a key generated on the local
|
||||
TPM, if available, and the null key otherwise) and place next to the installed kernel image in the
|
||||
ESP. This may be used to parameterize the installed kernel with arbitrary system credentials. Do not
|
||||
use this switch for sensitive data (such as passwords), use <option>--load-credential=</option>
|
||||
instead, see below. May be used multiple times to configure multiple credentials.</para>
|
||||
|
||||
<para>Note that three system credentials are propagated in similar fashion to the target system:
|
||||
the locale, keymap and timezone. This may be controlled by the relevant
|
||||
<option>--copy-locale=</option>, <option>--copy-keymap=</option> and <option>--copy-timezone=</option>
|
||||
options below.</para>
|
||||
|
||||
<para>See
|
||||
<citerefentry><refentrytitle>systemd.system-credentials</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
||||
for a list of well-known system credentials that may be propagated this way. (Note that you may pass
|
||||
arbitrary additional credentials this way, that can be consumed by any service of your
|
||||
choice, via the usual system credentials logic.)</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--load-credential=<replaceable>id</replaceable>:<replaceable>path</replaceable></option></term>
|
||||
|
||||
<listitem><para>Similar to <option>--set-credential=</option> but reads the credential value from a
|
||||
file on disk or an <constant>AF_UNIX</constant> socket in the file system. This is generally
|
||||
preferable for sensitive data, such as passwords.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--copy-locale=</option></term>
|
||||
<term><option>--copy-keymap=</option></term>
|
||||
<term><option>--copy-timezone=</option></term>
|
||||
|
||||
<listitem><para>These options take boolean parameters. They control whether the indicated system
|
||||
settings shall be propagated from the currently running system into the new target OS
|
||||
installation. These options default to true.</para>
|
||||
|
||||
<para>Typically, these three settings are the minimal settings that need to be configured during early
|
||||
boot of an installer medium in order to make the installer tool accessible to the user. The
|
||||
<citerefentry><refentrytitle>systemd-firstboot</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
tool may be used to query the user interactively when the OS install medium is booted for these
|
||||
properties. By propagating these settings to the target installation via system credentials they do
|
||||
not need to be queried again on first boot of the new installation.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--mute-console=</option></term>
|
||||
|
||||
<listitem><para>Takes a boolean argument. Controls whether to disable kernel and service manager log
|
||||
output to the console the installer is invoked on temporarily while running, in order to avoid
|
||||
interleaved output. Defaults to false.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
<xi:include href="standard-options.xml" xpointer="version" />
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Exit status</title>
|
||||
|
||||
<para>On success, 0 is returned, and a non-zero failure code otherwise.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Example</title>
|
||||
|
||||
<example>
|
||||
<title>Invoke the tool for a fully automatic non-interactive OS installation</title>
|
||||
|
||||
<programlisting>systemd-sysinstall \
|
||||
/dev/disk/by-id/nvme-Micron_MTFDKBA1T0TFH_214532D0CDA5 \
|
||||
--erase=yes \
|
||||
--confirm=no \
|
||||
--variables=yes \
|
||||
--load-credential=ssh.authorized_keys.root:my-ssh-key
|
||||
</programlisting>
|
||||
|
||||
<para>This installs the OS on the selected disk, erasing any previous contents, without confirmation,
|
||||
registers it in the firmware, and drops in the SSH key for the root user, read from the
|
||||
<filename>my-ssh-key</filename> file in the current directory.</para>
|
||||
</example>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para><simplelist type="inline">
|
||||
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-creds</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-firstboot</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd.system-credentials</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
|
||||
</simplelist></para>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
||||
@@ -1582,6 +1582,7 @@ foreach tuple : [
|
||||
['rfkill'],
|
||||
['smack'],
|
||||
['sysext'],
|
||||
['sysinstall'],
|
||||
['sysusers'],
|
||||
['timedated'],
|
||||
['timesyncd'],
|
||||
@@ -2144,6 +2145,7 @@ subdir('src/storagetm')
|
||||
subdir('src/sulogin-shell')
|
||||
subdir('src/sysctl')
|
||||
subdir('src/sysext')
|
||||
subdir('src/sysinstall')
|
||||
subdir('src/system-update-generator')
|
||||
subdir('src/systemctl')
|
||||
subdir('src/sysupdate')
|
||||
@@ -2928,6 +2930,7 @@ foreach tuple : [
|
||||
['resolve'],
|
||||
['rfkill'],
|
||||
['sysext'],
|
||||
['sysinstall'],
|
||||
['systemd-analyze', conf.get('ENABLE_ANALYZE') == 1],
|
||||
['sysupdate'],
|
||||
['sysupdated'],
|
||||
|
||||
@@ -109,6 +109,8 @@ option('sysupdate', type : 'feature', deprecated : { 'true' : 'enabled', 'false'
|
||||
option('sysupdated', type: 'combo', value : 'auto',
|
||||
choices : ['auto', 'enabled', 'disabled'],
|
||||
description : 'install the systemd-sysupdated service')
|
||||
option('sysinstall', type : 'boolean',
|
||||
description : 'install the systemd-sysinstall tool')
|
||||
|
||||
option('coredump', type : 'boolean',
|
||||
description : 'install the coredump handler')
|
||||
|
||||
@@ -54,6 +54,7 @@ foreach item : [
|
||||
['systemd-resolve', 'ENABLE_RESOLVE'],
|
||||
['systemd-run', ''],
|
||||
['systemd-sysext', 'ENABLE_SYSEXT'],
|
||||
['systemd-sysinstall', 'ENABLE_SYSINSTALL'],
|
||||
['systemd-vmspawn', 'ENABLE_VMSPAWN'],
|
||||
['systemd-vpick', ''],
|
||||
['timedatectl', 'ENABLE_TIMEDATED'],
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
# shellcheck shell=bash
|
||||
# systemd-sysinstall(8) completion -*- shell-script -*-
|
||||
# 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.
|
||||
#
|
||||
# systemd is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with systemd; If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
__contains_word() {
|
||||
local w word=$1; shift
|
||||
for w in "$@"; do
|
||||
[[ $w = "$word" ]] && return
|
||||
done
|
||||
}
|
||||
|
||||
__get_block_devices() {
|
||||
systemd-repart --list-devices 2>/dev/null
|
||||
}
|
||||
|
||||
_systemd_sysinstall() {
|
||||
local comps
|
||||
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
|
||||
local -A OPTS=(
|
||||
[STANDALONE]='-h --help --version'
|
||||
[ARG]='--welcome
|
||||
--chrome
|
||||
--erase
|
||||
--confirm
|
||||
--summary
|
||||
--reboot
|
||||
--variables
|
||||
--mute-console
|
||||
--copy-locale
|
||||
--copy-keymap
|
||||
--copy-timezone
|
||||
--definitions
|
||||
--kernel
|
||||
--set-credential
|
||||
--load-credential'
|
||||
)
|
||||
|
||||
_init_completion || return
|
||||
|
||||
if __contains_word "$prev" ${OPTS[ARG]}; then
|
||||
case $prev in
|
||||
--welcome|--chrome|--confirm|--summary|--reboot|--mute-console|--copy-locale|--copy-keymap|--copy-timezone)
|
||||
comps='yes no'
|
||||
;;
|
||||
--erase|--variables)
|
||||
comps='yes no auto'
|
||||
;;
|
||||
--definitions)
|
||||
comps=$(compgen -A directory -- "$cur")
|
||||
compopt -o filenames
|
||||
;;
|
||||
--kernel|--load-credential)
|
||||
comps=$(compgen -A file -- "$cur")
|
||||
compopt -o filenames
|
||||
;;
|
||||
--set-credential)
|
||||
comps=''
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ "$cur" = -* ]]; then
|
||||
COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") )
|
||||
return 0
|
||||
fi
|
||||
|
||||
comps=$(__get_block_devices)
|
||||
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
|
||||
compopt -o filenames
|
||||
return 0
|
||||
}
|
||||
|
||||
complete -F _systemd_sysinstall systemd-sysinstall
|
||||
@@ -0,0 +1,29 @@
|
||||
#compdef systemd-sysinstall
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
(( $+functions[_systemd-sysinstall_devices] )) ||
|
||||
_systemd-sysinstall_devices() {
|
||||
local -a _devices
|
||||
_devices=( ${(f)"$(systemd-repart --list-devices 2>/dev/null)"} )
|
||||
_wanted devices expl 'block device' compadd -a _devices
|
||||
}
|
||||
|
||||
_arguments \
|
||||
'(- *)'{-h,--help}'[Show help text]' \
|
||||
'(- *)--version[Show package version]' \
|
||||
'--welcome=[Show welcome text]:boolean:(yes no)' \
|
||||
'--chrome=[Show colored bars at top and bottom of the terminal]:boolean:(yes no)' \
|
||||
'--erase=[Erase target disk before installation]:boolean:(yes no auto)' \
|
||||
'--confirm=[Query for confirmation before installation]:boolean:(yes no)' \
|
||||
'--summary=[Show summary before installation]:boolean:(yes no)' \
|
||||
'--reboot=[Reboot system after installation]:boolean:(yes no)' \
|
||||
'--variables=[Register installation in firmware variables]:boolean:(yes no auto)' \
|
||||
'--mute-console=[Mute kernel/PID 1 console output during installation]:boolean:(yes no)' \
|
||||
'--copy-locale=[Copy current locale to target system]:boolean:(yes no)' \
|
||||
'--copy-keymap=[Copy current keymap to target system]:boolean:(yes no)' \
|
||||
'--copy-timezone=[Copy current timezone to target system]:boolean:(yes no)' \
|
||||
'--definitions=[Find partition definitions in directory]:directory:_directories' \
|
||||
'--kernel=[Kernel image to install]:kernel image:_files' \
|
||||
'--set-credential=[Install a credential with a literal value]: : _message "ID:VALUE"' \
|
||||
'--load-credential=[Load credential from a file or AF_UNIX socket]: : _message "ID:PATH"' \
|
||||
'*::block device:_systemd-sysinstall_devices'
|
||||
@@ -43,6 +43,7 @@ foreach item : [
|
||||
['_systemd-nspawn', ''],
|
||||
['_systemd-path', ''],
|
||||
['_systemd-run', ''],
|
||||
['_systemd-sysinstall', 'ENABLE_SYSINSTALL'],
|
||||
['_systemd-tmpfiles', 'ENABLE_TMPFILES'],
|
||||
['_timedatectl', 'ENABLE_TIMEDATED'],
|
||||
['_udevadm', ''],
|
||||
|
||||
@@ -1697,6 +1697,16 @@ int get_timezone(char **ret) {
|
||||
return strdup_to(ret, e);
|
||||
}
|
||||
|
||||
int get_timezone_prefer_env(char **ret) {
|
||||
assert(ret);
|
||||
|
||||
const char *e = getenv("TZ");
|
||||
if (e && e[0] == ':' && timezone_is_valid(e + 1, LOG_DEBUG))
|
||||
return strdup_to(ret, e + 1);
|
||||
|
||||
return get_timezone(ret);
|
||||
}
|
||||
|
||||
const char* etc_localtime(void) {
|
||||
static const char *cached = NULL;
|
||||
|
||||
|
||||
@@ -178,6 +178,7 @@ bool clock_supported(clockid_t clock);
|
||||
usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to);
|
||||
|
||||
int get_timezone(char **ret);
|
||||
int get_timezone_prefer_env(char **ret);
|
||||
const char* etc_localtime(void);
|
||||
|
||||
int mktime_or_timegm_usec(struct tm *tm, bool utc, usec_t *ret);
|
||||
|
||||
+33
-16
@@ -25,6 +25,7 @@
|
||||
#include "hashmap.h"
|
||||
#include "id128-util.h"
|
||||
#include "io-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "json-util.h"
|
||||
#include "kernel-image.h"
|
||||
#include "log.h"
|
||||
@@ -44,6 +45,7 @@ typedef struct ExtraFile {
|
||||
/* The source and the temporary file we copy it into */
|
||||
int source_fd, temp_fd;
|
||||
char *filename, *temp_filename;
|
||||
struct iovec data; /* Alternative to 'source_fd': literal data */
|
||||
} ExtraFile;
|
||||
|
||||
#define EXTRA_FILE_NULL \
|
||||
@@ -115,6 +117,7 @@ static void extra_file_done(ExtraFile *x) {
|
||||
x->temp_fd = safe_close(x->temp_fd);
|
||||
x->filename = mfree(x->filename);
|
||||
x->temp_filename = mfree(x->temp_filename);
|
||||
iovec_done(&x->data);
|
||||
}
|
||||
|
||||
static void profile_done(Profile *p) {
|
||||
@@ -366,7 +369,8 @@ static int link_context_pick_entry_token(LinkContext *c) {
|
||||
}
|
||||
|
||||
static int begin_copy_file(
|
||||
int source_fd,
|
||||
int source_fd, /* Either the source fd is specified, or the 'data' below, not both */
|
||||
const struct iovec *data,
|
||||
const char *filename,
|
||||
int target_dir_fd,
|
||||
int *ret_tmpfile_fd,
|
||||
@@ -374,7 +378,6 @@ static int begin_copy_file(
|
||||
|
||||
int r;
|
||||
|
||||
assert(source_fd >= 0);
|
||||
assert(filename);
|
||||
assert(target_dir_fd >= 0);
|
||||
assert(ret_tmpfile_fd);
|
||||
@@ -398,11 +401,18 @@ static int begin_copy_file(
|
||||
|
||||
CLEANUP_TMPFILE_AT(target_dir_fd, t);
|
||||
|
||||
r = copy_bytes(source_fd, write_fd, UINT64_MAX, COPY_REFLINK|COPY_SEEK0_SOURCE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy data into '%s': %m", filename);
|
||||
if (source_fd >= 0) {
|
||||
r = copy_bytes(source_fd, write_fd, UINT64_MAX, COPY_REFLINK|COPY_SEEK0_SOURCE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy data into '%s': %m", filename);
|
||||
|
||||
(void) copy_times(source_fd, write_fd, /* flags= */ 0);
|
||||
} else if (iovec_is_set(data)) {
|
||||
r = loop_write(write_fd, data->iov_base, data->iov_len);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to write data into '%s': %m", filename);
|
||||
}
|
||||
|
||||
(void) copy_times(source_fd, write_fd, /* flags= */ 0);
|
||||
(void) fchmod(write_fd, 0644);
|
||||
|
||||
*ret_tmpfile_fd = TAKE_FD(write_fd);
|
||||
@@ -824,6 +834,7 @@ static int run_link_now(LinkContext *c) {
|
||||
|
||||
r = begin_copy_file(
|
||||
c->kernel_fd,
|
||||
/* data= */ NULL,
|
||||
c->kernel_filename,
|
||||
c->entry_token_dir_fd,
|
||||
&c->kernel_temp_fd,
|
||||
@@ -834,6 +845,7 @@ static int run_link_now(LinkContext *c) {
|
||||
FOREACH_ARRAY(x, c->extra, c->n_extra) {
|
||||
r = begin_copy_file(
|
||||
x->source_fd,
|
||||
&x->data,
|
||||
x->filename,
|
||||
c->entry_token_dir_fd,
|
||||
&x->temp_fd,
|
||||
@@ -1043,7 +1055,8 @@ static int dispatch_extras(const char *name, sd_json_variant *v, sd_json_dispatc
|
||||
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "filename", SD_JSON_VARIANT_STRING, json_dispatch_loader_entry_resource_filename, offsetof(ExtraParameters, extra_file.filename), SD_JSON_MANDATORY },
|
||||
{ "fileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, offsetof(ExtraParameters, fd_index), SD_JSON_MANDATORY },
|
||||
{ "fileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, offsetof(ExtraParameters, fd_index), 0 },
|
||||
{ "data", SD_JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, offsetof(ExtraParameters, extra_file.data), 0 },
|
||||
{},
|
||||
};
|
||||
|
||||
@@ -1051,17 +1064,21 @@ static int dispatch_extras(const char *name, sd_json_variant *v, sd_json_dispatc
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
xp.extra_file.source_fd = sd_varlink_peek_dup_fd(c->link, xp.fd_index);
|
||||
if (xp.extra_file.source_fd < 0)
|
||||
return log_debug_errno(xp.extra_file.source_fd, "Failed to acquire extra fd from Varlink: %m");
|
||||
|
||||
r = fd_verify_safe_flags(xp.extra_file.source_fd);
|
||||
if (r < 0)
|
||||
if (iovec_is_set(&xp.extra_file.data) == (xp.fd_index != UINT_MAX))
|
||||
return sd_varlink_error_invalid_parameter_name(c->link, name);
|
||||
if (xp.fd_index != UINT_MAX) {
|
||||
xp.extra_file.source_fd = sd_varlink_peek_dup_fd(c->link, xp.fd_index);
|
||||
if (xp.extra_file.source_fd < 0)
|
||||
return log_debug_errno(xp.extra_file.source_fd, "Failed to acquire extra fd from Varlink: %m");
|
||||
|
||||
r = fd_verify_regular(xp.extra_file.source_fd);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to validate that the extra file is a regular file descriptor: %m");
|
||||
r = fd_verify_safe_flags(xp.extra_file.source_fd);
|
||||
if (r < 0)
|
||||
return sd_varlink_error_invalid_parameter_name(c->link, name);
|
||||
|
||||
r = fd_verify_regular(xp.extra_file.source_fd);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to validate that the extra file is a regular file descriptor: %m");
|
||||
}
|
||||
|
||||
if (!GREEDY_REALLOC(c->context.extra, c->context.n_extra+1))
|
||||
return log_oom();
|
||||
|
||||
@@ -153,8 +153,10 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
BootEntryExtraFile,
|
||||
SD_VARLINK_FIELD_COMMENT("The name of the extra file"),
|
||||
SD_VARLINK_DEFINE_FIELD(filename, SD_VARLINK_STRING, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("Index into array of file descriptors, pointing to a file descriptor referencing the extra file."),
|
||||
SD_VARLINK_DEFINE_FIELD(fileDescriptor, SD_VARLINK_INT, 0));
|
||||
SD_VARLINK_FIELD_COMMENT("Index into array of file descriptors, pointing to a file descriptor referencing the extra file to copy in. Either this or the 'data' field below must be set – not both, not neither."),
|
||||
SD_VARLINK_DEFINE_FIELD(fileDescriptor, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Literal data to place in the extra file."),
|
||||
SD_VARLINK_DEFINE_FIELD(data, SD_VARLINK_STRING, SD_VARLINK_NULLABLE));
|
||||
|
||||
static SD_VARLINK_DEFINE_METHOD(
|
||||
Link,
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
executables += [
|
||||
executable_template + {
|
||||
'name' : 'systemd-sysinstall',
|
||||
'public' : true,
|
||||
'conditions' : ['ENABLE_SYSINSTALL'],
|
||||
'sources' : files('sysinstall.c'),
|
||||
},
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
+183
@@ -0,0 +1,183 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
if ! command -v systemd-sysinstall >/dev/null; then
|
||||
echo "systemd-sysinstall not found, skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! command -v systemd-repart >/dev/null; then
|
||||
echo "systemd-repart not found, skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! command -v bootctl >/dev/null; then
|
||||
echo "bootctl not found, skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! command -v ukify >/dev/null; then
|
||||
echo "ukify not found, skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ ! -d /usr/lib/systemd/boot/efi ]]; then
|
||||
echo "sd-boot is not installed, skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# We need a real environment to fiddle with loop devices.
|
||||
if systemd-detect-virt -cq; then
|
||||
echo "Running in a container, skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# shellcheck source=test/units/util.sh
|
||||
. "$(dirname "$0")"/util.sh
|
||||
|
||||
WORKDIR="$(mktemp --directory /tmp/test-sysinstall.XXXXXXXXXX)"
|
||||
LOOPDEV=""
|
||||
MOUNTED=0
|
||||
|
||||
cleanup() {
|
||||
set +e
|
||||
if [[ "$MOUNTED" -eq 1 ]]; then
|
||||
umount -R "$WORKDIR/mnt"
|
||||
MOUNTED=0
|
||||
fi
|
||||
if [[ -n "$LOOPDEV" ]]; then
|
||||
systemd-dissect --detach "$LOOPDEV"
|
||||
LOOPDEV=""
|
||||
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"
|
||||
|
||||
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"
|
||||
|
||||
# 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"
|
||||
|
||||
# 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
|
||||
[Partition]
|
||||
Type=esp
|
||||
Format=vfat
|
||||
SizeMinBytes=64M
|
||||
SizeMaxBytes=64M
|
||||
EOF
|
||||
|
||||
cat >"$DEFS/20-root.conf" <<EOF
|
||||
[Partition]
|
||||
Type=root
|
||||
Format=ext4
|
||||
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"
|
||||
|
||||
# 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"
|
||||
|
||||
# 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"
|
||||
|
||||
# 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
|
||||
|
||||
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
|
||||
|
||||
# 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
|
||||
|
||||
# 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"
|
||||
|
||||
# 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
|
||||
@@ -240,6 +240,7 @@ units = [
|
||||
},
|
||||
{ 'file' : 'sysinit.target' },
|
||||
{ 'file' : 'syslog.socket' },
|
||||
{ 'file' : 'system-install.target' },
|
||||
{
|
||||
'file' : 'system-systemd\\x2dcryptsetup.slice',
|
||||
'conditions' : ['HAVE_LIBCRYPTSETUP'],
|
||||
@@ -778,6 +779,11 @@ units = [
|
||||
'file' : 'systemd-sysext@.service',
|
||||
'conditions' : ['ENABLE_SYSEXT'],
|
||||
},
|
||||
{
|
||||
'file' : 'systemd-sysinstall.service',
|
||||
'conditions' : ['ENABLE_SYSINSTALL'],
|
||||
'symlinks' : ['system-install.target.wants/'],
|
||||
},
|
||||
{
|
||||
'file' : 'systemd-sysupdate-reboot.service.in',
|
||||
'conditions' : ['ENABLE_SYSUPDATE'],
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# 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 Installer
|
||||
Documentation=man:systemd-sysinstall(8)
|
||||
Requires=sysinit.target
|
||||
After=sysinit.target
|
||||
AllowIsolate=yes
|
||||
@@ -0,0 +1,22 @@
|
||||
# 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)
|
||||
Wants=systemd-logind.service
|
||||
After=systemd-logind.service
|
||||
|
||||
[Service]
|
||||
ExecStart=systemd-sysinstall --variables=yes --reboot=yes --mute-console=yes
|
||||
StandardOutput=tty
|
||||
StandardInput=tty
|
||||
StandardError=tty
|
||||
TTYReset=yes
|
||||
FailureAction=halt
|
||||
Reference in New Issue
Block a user