Merge pull request #31507 from poettering/import-modernize

importd: various modernizations
This commit is contained in:
Lennart Poettering 2024-03-01 23:42:32 +01:00 committed by GitHub
commit 0068131bf5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
47 changed files with 3132 additions and 1409 deletions

12
TODO
View file

@ -133,6 +133,12 @@ Features:
* add a new specifier to unit files that figures out the DDI the unit file is
from, tracing through overlayfs, DM, loopback block device.
* importd/importctl
- import generator
- port tar handling to libarchive
- add varlink interface
- download images into .v/ dirs
* in os-release define a field that can be initialized at build time from
SOURCE_DATE_EPOCH (maybe even under that name?). Would then be used to
initialize the timestamp logic of ConditionNeedsUpdate=.
@ -153,10 +159,6 @@ Features:
pidfd, so that we can reasonably robustly do this. Would only cover the
execution environment like namespaces, but not the privilege settings.
* add a generic client to importd called importctl, then make machinectl just
chain-exec() it. Make sure importd/importctl can be used for sysext images,
portable images too.
* varlink: extend varlink IDL macros to include documentation strings
* Introduce a CGroupRef structure, inspired by PidRef. Should contain cgroup
@ -1541,8 +1543,6 @@ Features:
that our log messages could contain clickable links for example for unit
files and suchlike we operate on.
* importd: add ability download images for portabled + sysext
* add support for "portablectl attach http://foobar.com/waaa.raw (i.e. importd integration)
* sync dynamic uids/gids between host+portable srvice (i.e. if DynamicUser=1 is set for a service, make sure that the

442
man/importctl.xml Normal file
View file

@ -0,0 +1,442 @@
<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % entities SYSTEM "custom-entities.ent" >
%entities;
]>
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="importctl" conditional='ENABLE_MACHINED'
xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>importctl</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>importctl</refentrytitle>
<manvolnum>1</manvolnum>
</refmeta>
<refnamediv>
<refname>importctl</refname>
<refpurpose>Download, import or export disk images</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>importctl</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="req">COMMAND</arg>
<arg choice="opt" rep="repeat">NAME</arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><command>importctl</command> may be used to download, import export disk images via
<citerefentry><refentrytitle>systemd-importd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<para><command>importctl</command> operates both on block-level disk images (such as DDIs) as well as
file-system-level images (tarballs). It supports disk images are one of the four following
classes:</para>
<itemizedlist>
<listitem><para>VM images or full OS container images, that may be run via
<citerefentry><refentrytitle>systemd-vmspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry> or
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>, and
managed via
<citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para></listitem>
<listitem><para>Portable service images, that may be attached an managed via
<citerefentry><refentrytitle>portablectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para></listitem>
<listitem><para>System extension (sysext) images, that may be activated via
<citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
<listitem><para>Configuration extension (confext) images, that may be activated via
<citerefentry><refentrytitle>systemd-confext</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
</itemizedlist>
<para>When images are downloaded or imported they are placed in the following directories, depending on
the <option>--class=</option> parameter:</para>
<table>
<title>Classes and Directories</title>
<tgroup cols='2'>
<colspec colname='class' />
<colspec colname='directory' />
<thead>
<row>
<entry>Class</entry>
<entry>Directory</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>machine</literal></entry>
<entry><filename>/var/lib/machines/</filename></entry>
</row>
<row>
<entry><literal>portable</literal></entry>
<entry><filename>/var/lib/portables/</filename></entry>
</row>
<row>
<entry><literal>sysext</literal></entry>
<entry><filename>/var/lib/extensions/</filename></entry>
</row>
<row>
<entry><literal>confext</literal></entry>
<entry><filename>/var/lib/confexts/</filename></entry>
</row>
</tbody>
</tgroup>
</table>
</refsect1>
<refsect1>
<title>Commands</title>
<para>The following commands are understood:</para>
<variablelist>
<varlistentry>
<term><command>pull-tar</command> <replaceable>URL</replaceable> [<replaceable>NAME</replaceable>]</term>
<listitem><para>Downloads a <filename>.tar</filename> image from the specified URL, and makes it
available under the specified local name in the image directory for the selected
<option>--class=</option>. The URL must be of type <literal>http://</literal> or
<literal>https://</literal>, and must refer to a <filename>.tar</filename>,
<filename>.tar.gz</filename>, <filename>.tar.xz</filename> or <filename>.tar.bz2</filename> archive
file. If the local image name is omitted, it is automatically derived from the last component of the
URL, with its suffix removed.</para>
<para>The image is verified before it is made available, unless <option>--verify=no</option> is
specified. Verification is done either via an inline signed file with the name of the image and the
suffix <filename>.sha256</filename> or via separate <filename>SHA256SUMS</filename> and
<filename>SHA256SUMS.gpg</filename> files. The signature files need to be made available on the same
web server, under the same URL as the <filename>.tar</filename> file. With
<option>--verify=checksum</option>, only the SHA256 checksum for the file is verified, based on the
<filename>.sha256</filename> suffixed file or the <filename>SHA256SUMS</filename> file. With
<option>--verify=signature</option>, the sha checksum file is first verified with the inline
signature in the <filename>.sha256</filename> file or the detached GPG signature file
<filename>SHA256SUMS.gpg</filename>. The public key for this verification step needs to be available
in <filename>/usr/lib/systemd/import-pubring.gpg</filename> or
<filename>/etc/systemd/import-pubring.gpg</filename>.</para>
<para>If <option>-keep-download=yes</option> is specified the image will be downloaded and stored in
a read-only subvolume/directory in the image directory that is named after the specified URL and its
HTTP etag. A writable snapshot is then taken from this subvolume, and named after the specified local
name. This behavior ensures that creating multiple instances of the same URL is efficient, as
multiple downloads are not necessary. In order to create only the read-only image, and avoid creating
its writable snapshot, specify <literal>-</literal> as local name.</para>
<para>Note that pressing C-c during execution of this command will not abort the download. Use
<command>cancel-transfer</command>, described below.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><command>pull-raw</command> <replaceable>URL</replaceable> [<replaceable>NAME</replaceable>]</term>
<listitem><para>Downloads a <filename>.raw</filename> disk image from the specified URL, and makes it
available under the specified local name in the image directory for the selected
<option>--class=</option>. The URL must be of type <literal>http://</literal> or
<literal>https://</literal>. The image must either be a <filename>.qcow2</filename> or raw disk
image, optionally compressed as <filename>.gz</filename>, <filename>.xz</filename>, or
<filename>.bz2</filename>. If the local name is omitted, it is automatically derived from the last
component of the URL, with its suffix removed.</para>
<para>Image verification is identical for raw and tar images (see above).</para>
<para>If the downloaded image is in <filename>.qcow2</filename> format it is converted into a raw
image file before it is made available.</para>
<para>If <option>-keep-download=yes</option> is specified the image will be downloaded and stored in
a read-only file in the image directory that is named after the specified URL and its HTTP etag. A
writable copy is then made from this file, and named after the specified local name. This behavior
ensures that creating multiple instances of the same URL is efficient, as multiple downloads are not
necessary. In order to create only the read-only image, and avoid creating its writable copy,
specify <literal>-</literal> as local name.</para>
<para>Note that pressing C-c during execution of this command will not abort the download. Use
<command>cancel-transfer</command>, described below.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><command>import-tar</command> <replaceable>FILE</replaceable> [<replaceable>NAME</replaceable>]</term>
<term><command>import-raw</command> <replaceable>FILE</replaceable> [<replaceable>NAME</replaceable>]</term>
<listitem><para>Imports a TAR or RAW image, and places it under the specified name in the image
directory for the image class selected via <option>--class=</option>. When
<command>import-tar</command> is used, the file specified as the first argument should be a tar
archive, possibly compressed with xz, gzip or bzip2. It will then be unpacked into its own
subvolume/directory. When <command>import-raw</command> is used, the file should be a qcow2 or raw
disk image, possibly compressed with xz, gzip or bzip2. If the second argument (the resulting image
name) is not specified, it is automatically derived from the file name. If the filename is passed as
<literal>-</literal>, the image is read from standard input, in which case the second argument is
mandatory.</para>
<para>No cryptographic validation is done when importing the images.</para>
<para>Much like image downloads, ongoing imports may be listed with <command>list</command>
and aborted with <command>cancel-transfer</command>.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><command>import-fs</command> <replaceable>DIRECTORY</replaceable> [<replaceable>NAME</replaceable>]</term>
<listitem><para>Imports an image stored in a local directory into the image directory for the image
class selected via <option>--class=</option> and operates similarly to <command>import-tar</command>
or <command>import-raw</command>, but the first argument is the source directory. If supported, this
command will create a btrfs snapshot or subvolume for the new image.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><command>export-tar</command> <replaceable>NAME</replaceable> [<replaceable>FILE</replaceable>]</term>
<term><command>export-raw</command> <replaceable>NAME</replaceable> [<replaceable>FILE</replaceable>]</term>
<listitem><para>Exports a TAR or RAW image and stores it in the specified file. The first parameter
should be an image name. The second parameter should be a file path the TAR or RAW
image is written to. If the path ends in <literal>.gz</literal>, the file is compressed with gzip, if
it ends in <literal>.xz</literal>, with xz, and if it ends in <literal>.bz2</literal>, with bzip2. If
the path ends in neither, the file is left uncompressed. If the second argument is missing, the image
is written to standard output. The compression may also be explicitly selected with the
<option>--format=</option> switch. This is in particular useful if the second parameter is left
unspecified.</para>
<para>Much like image downloads and imports, ongoing exports may be listed with
<command>list</command> and aborted with <command>cancel-transfer</command>.</para>
<para>Note that, currently, only directory and subvolume images may be exported as TAR images, and
only raw disk images as RAW images.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><command>list-transfer</command></term>
<listitem><para>Shows a list of image downloads, imports and exports that are currently in
progress.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><command>cancel-transfer</command> <replaceable>ID</replaceable></term>
<listitem><para>Aborts a download, import or export of the image with the specified ID. To list
ongoing transfers and their IDs, use <command>list</command>. </para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><command>list-images</command></term>
<listitem><para>Shows a list of already downloaded/imported images.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Options</title>
<para>The following options are understood:</para>
<variablelist>
<varlistentry>
<term><option>--read-only</option></term>
<listitem>
<para>When used with <command>pull-raw</command>, <command>pull-tar</command>,
<command>import-raw</command>, <command>import-tar</command> or <command>import-fs</command> a
read-only image is created.</para>
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--verify=</option></term>
<listitem><para>When downloading an image, specify whether the image shall be verified before it is
made available. Takes one of <literal>no</literal>, <literal>checksum</literal> and
<literal>signature</literal>. If <literal>no</literal>, no verification is done. If
<literal>checksum</literal> is specified, the download is checked for integrity after the transfer is
complete, but no signatures are verified. If <literal>signature</literal> is specified, the checksum
is verified and the image's signature is checked against a local keyring of trustable vendors. It is
strongly recommended to set this option to <literal>signature</literal> if the server and protocol
support this. Defaults to <literal>signature</literal>.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--force</option></term>
<listitem><para>When downloading an image, and a local copy by the specified local name already
exists, delete it first and replace it by the newly downloaded image.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--format=</option></term>
<listitem><para>When used with the <option>export-tar</option> or <option>export-raw</option>
commands, specifies the compression format to use for the resulting file. Takes one of
<literal>uncompressed</literal>, <literal>xz</literal>, <literal>gzip</literal>,
<literal>bzip2</literal>. By default, the format is determined automatically from the output image
file name passed.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><option>-q</option></term>
<term><option>--quiet</option></term>
<listitem><para>Suppresses additional informational output while running.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<xi:include href="user-system-options.xml" xpointer="host" />
<varlistentry>
<term><option>-M</option></term>
<term><option>--machine=</option></term>
<listitem><para>Connect to
<citerefentry><refentrytitle>systemd-import.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
running in a local container, to perform the specified operation within the container.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--class=</option></term>
<term><option>-m</option></term>
<term><option>-P</option></term>
<term><option>-S</option></term>
<term><option>-C</option></term>
<listitem><para>Selects the image class for the downloaded images. This primarily selects the
directory to download into. The <option>--class=</option> switch takes <literal>machine</literal>,
<literal>portable</literal>, <literal>sysext</literal> or <literal>confext</literal> as argument. The
short options <option>-m</option>, <option>-P</option>, <option>-S</option>, <option>-C</option> are
shortcuts for <option>--class=machine</option>, <option>--class=portable</option>,
<option>--class=sysext</option>, <option>--class=confext</option>.</para>
<para>Note that <option>--keep-download=</option> defaults to true for
<option>--class=machine</option> and false otherwise, see below.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--keep-download=</option></term>
<term><option>-N</option></term>
<listitem><para>Takes a boolean argument. When specified with <command>pull-raw</command> or
<command>pull-tar</command>, selects whether to download directly into the specified local image
name, or whether to download into a read-only copy first of which to make a writable copy after the
download is completed. Defaults to true for <option>--class=machine</option>, false otherwise.</para>
<para>The <option>-N</option> switch is a shortcut for <option>--keep-download=no</option>.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="json" />
<xi:include href="standard-options.xml" xpointer="j" />
<xi:include href="standard-options.xml" xpointer="no-pager" />
<xi:include href="standard-options.xml" xpointer="no-legend" />
<xi:include href="standard-options.xml" xpointer="no-ask-password" />
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
</refsect1>
<refsect1>
<title>Examples</title>
<example id="example-import-tar">
<title>Download an Ubuntu TAR image and open a shell in it</title>
<programlisting># importctl pull-tar -mN https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64-root.tar.xz
# systemd-nspawn -M jammy-server-cloudimg-amd64-root</programlisting>
<para>This downloads and verifies the specified <filename>.tar</filename> image, and then uses
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry> to
open a shell in it.</para>
</example>
<example id="example-import-raw">
<title>Download an Ubuntu RAW image, set a root password in it, start
it as a service</title>
<programlisting># importctl pull-raw -mN \
https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64-disk-kvm.img \
jammy
# systemd-firstboot --image=/var/lib/machines/jammy.raw --prompt-root-password --force
# machinectl start jammy
# machinectl login jammy</programlisting>
<para>This downloads the specified <filename>.raw</filename> image and makes it available under the
local name <literal>jammy</literal>. Then, a root password is set with
<citerefentry><refentrytitle>systemd-firstboot</refentrytitle><manvolnum>1</manvolnum></citerefentry>. Afterwards
the machine is started as system service. With the last command a login prompt into the container is
requested.</para>
</example>
<example id="example-export-tar">
<title>Exports a container image as tar file</title>
<programlisting># importctl export-tar -m fedora myfedora.tar.xz</programlisting>
<para>Exports the container <literal>fedora</literal> as an xz-compressed tar file
<filename>myfedora.tar.xz</filename> into the current directory.</para>
</example>
</refsect1>
<refsect1>
<title>Exit status</title>
<para>On success, 0 is returned, a non-zero failure code
otherwise.</para>
</refsect1>
<xi:include href="common-variables.xml" />
<refsect1>
<title>See Also</title>
<para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-importd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-vmspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>portablectl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-confext</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry project='die-net'><refentrytitle>tar</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry project='die-net'><refentrytitle>xz</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry project='die-net'><refentrytitle>gzip</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry project='die-net'><refentrytitle>bzip2</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>
</refentry>

View file

@ -80,6 +80,9 @@
<listitem><para>The file system tree of the host OS itself.</para></listitem>
</itemizedlist>
<para>Images may be downloaded, imported and exported via the
<citerefentry><refentrytitle>importctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
tool.</para>
</refsect1>
<refsect1>
@ -87,8 +90,9 @@
<para>The following commands are understood:</para>
<refsect2><title>Machine Commands</title><variablelist>
<refsect2><title>Machine Commands</title>
<variablelist>
<varlistentry>
<term><command>list</command></term>
@ -361,8 +365,9 @@
</varlistentry>
</variablelist></refsect2>
<refsect2><title>Image Commands</title><variablelist>
<refsect2><title>Image Commands</title>
<variablelist>
<varlistentry>
<term><command>list-images</command></term>
@ -515,7 +520,7 @@
<para>When combined with the <option>--all</option> switch removes all images, not just hidden ones. This
command effectively empties <filename>/var/lib/machines/</filename>.</para>
<para>Note that commands such as <command>machinectl pull-tar</command> or <command>machinectl
<para>Note that commands such as <command>importctl pull-tar</command> or <command>importctl
pull-raw</command> usually create hidden, read-only, unmodified machine images from the downloaded image first,
before cloning a writable working copy of it, in order to avoid duplicate downloads in case of images that are
reused multiple times. Use <command>machinectl clean</command> to remove old, hidden images created this
@ -525,197 +530,6 @@
</varlistentry>
</variablelist></refsect2>
<refsect2><title>Image Transfer Commands</title><variablelist>
<varlistentry>
<term><command>pull-tar</command> <replaceable>URL</replaceable> [<replaceable>NAME</replaceable>]</term>
<listitem><para>Downloads a <filename>.tar</filename>
container image from the specified URL, and makes it available
under the specified local machine name. The URL must be of
type <literal>http://</literal> or
<literal>https://</literal>, and must refer to a
<filename>.tar</filename>, <filename>.tar.gz</filename>,
<filename>.tar.xz</filename> or <filename>.tar.bz2</filename>
archive file. If the local machine name is omitted, it
is automatically derived from the last component of the URL,
with its suffix removed.</para>
<para>The image is verified before it is made available, unless
<option>--verify=no</option> is specified.
Verification is done either via an inline signed file with the name
of the image and the suffix <filename>.sha256</filename> or via
separate <filename>SHA256SUMS</filename> and
<filename>SHA256SUMS.gpg</filename> files.
The signature files need to be made available on the same web
server, under the same URL as the <filename>.tar</filename> file.
With <option>--verify=checksum</option>, only the SHA256 checksum
for the file is verified, based on the <filename>.sha256</filename>
suffixed file or the <filename>SHA256SUMS</filename> file.
With <option>--verify=signature</option>, the sha checksum file is
first verified with the inline signature in the
<filename>.sha256</filename> file or the detached GPG signature file
<filename>SHA256SUMS.gpg</filename>.
The public key for this verification step needs to be available in
<filename>/usr/lib/systemd/import-pubring.gpg</filename> or
<filename>/etc/systemd/import-pubring.gpg</filename>.</para>
<para>The container image will be downloaded and stored in a
read-only subvolume in
<filename>/var/lib/machines/</filename> that is named after
the specified URL and its HTTP etag. A writable snapshot is
then taken from this subvolume, and named after the specified
local name. This behavior ensures that creating multiple
container instances of the same URL is efficient, as multiple
downloads are not necessary. In order to create only the
read-only image, and avoid creating its writable snapshot,
specify <literal>-</literal> as local machine name.</para>
<para>Note that the read-only subvolume is prefixed with
<filename>.tar-</filename>, and is thus not shown by
<command>list-images</command>, unless <option>--all</option>
is passed.</para>
<para>Note that pressing C-c during execution of this command
will not abort the download. Use
<command>cancel-transfer</command>, described
below.</para>
<xi:include href="version-info.xml" xpointer="v219"/></listitem>
</varlistentry>
<varlistentry>
<term><command>pull-raw</command> <replaceable>URL</replaceable> [<replaceable>NAME</replaceable>]</term>
<listitem><para>Downloads a <filename>.raw</filename>
container or VM disk image from the specified URL, and makes
it available under the specified local machine name. The URL
must be of type <literal>http://</literal> or
<literal>https://</literal>. The container image must either
be a <filename>.qcow2</filename> or raw disk image, optionally
compressed as <filename>.gz</filename>,
<filename>.xz</filename>, or <filename>.bz2</filename>. If the
local machine name is omitted, it is automatically
derived from the last component of the URL, with its suffix
removed.</para>
<para>Image verification is identical for raw and tar images
(see above).</para>
<para>If the downloaded image is in
<filename>.qcow2</filename> format it is converted into a raw
image file before it is made available.</para>
<para>Downloaded images of this type will be placed as
read-only <filename>.raw</filename> file in
<filename>/var/lib/machines/</filename>. A local, writable
(reflinked) copy is then made under the specified local
machine name. To omit creation of the local, writable copy
pass <literal>-</literal> as local machine name.</para>
<para>Similarly to the behavior of <command>pull-tar</command>, the read-only image is prefixed with
<filename>.raw-</filename>, and thus not shown by <command>list-images</command>, unless
<option>--all</option> is passed.</para>
<para>Note that pressing C-c during execution of this command
will not abort the download. Use
<command>cancel-transfer</command>, described
below.</para>
<xi:include href="version-info.xml" xpointer="v219"/></listitem>
</varlistentry>
<varlistentry>
<term><command>import-tar</command> <replaceable>FILE</replaceable> [<replaceable>NAME</replaceable>]</term>
<term><command>import-raw</command> <replaceable>FILE</replaceable> [<replaceable>NAME</replaceable>]</term>
<listitem><para>Imports a TAR or RAW container or VM image,
and places it under the specified name in
<filename>/var/lib/machines/</filename>. When
<command>import-tar</command> is used, the file specified as
the first argument should be a tar archive, possibly compressed
with xz, gzip or bzip2. It will then be unpacked into its own
subvolume in <filename>/var/lib/machines/</filename>. When
<command>import-raw</command> is used, the file should be a
qcow2 or raw disk image, possibly compressed with xz, gzip or
bzip2. If the second argument (the resulting image name) is
not specified, it is automatically derived from the file
name. If the filename is passed as <literal>-</literal>, the
image is read from standard input, in which case the second
argument is mandatory.</para>
<para>Optionally, the <option>--read-only</option> switch may be used to create a read-only container or VM
image. No cryptographic validation is done when importing the images.</para>
<para>Much like image downloads, ongoing imports may be listed
with <command>list-transfers</command> and aborted with
<command>cancel-transfer</command>.</para>
<xi:include href="version-info.xml" xpointer="v220"/></listitem>
</varlistentry>
<varlistentry>
<term><command>import-fs</command> <replaceable>DIRECTORY</replaceable> [<replaceable>NAME</replaceable>]</term>
<listitem><para>Imports a container image stored in a local directory into
<filename>/var/lib/machines/</filename>, operates similarly to <command>import-tar</command> or
<command>import-raw</command>, but the first argument is the source directory. If supported, this
command will create a btrfs snapshot or subvolume for the new image.</para>
<xi:include href="version-info.xml" xpointer="v240"/></listitem>
</varlistentry>
<varlistentry>
<term><command>export-tar</command> <replaceable>NAME</replaceable> [<replaceable>FILE</replaceable>]</term>
<term><command>export-raw</command> <replaceable>NAME</replaceable> [<replaceable>FILE</replaceable>]</term>
<listitem><para>Exports a TAR or RAW container or VM image and
stores it in the specified file. The first parameter should be
a VM or container image name. The second parameter should be a
file path the TAR or RAW image is written to. If the path ends
in <literal>.gz</literal>, the file is compressed with gzip, if
it ends in <literal>.xz</literal>, with xz, and if it ends in
<literal>.bz2</literal>, with bzip2. If the path ends in
neither, the file is left uncompressed. If the second argument
is missing, the image is written to standard output. The
compression may also be explicitly selected with the
<option>--format=</option> switch. This is in particular
useful if the second parameter is left unspecified.</para>
<para>Much like image downloads and imports, ongoing exports
may be listed with <command>list-transfers</command> and
aborted with
<command>cancel-transfer</command>.</para>
<para>Note that, currently, only directory and subvolume images
may be exported as TAR images, and only raw disk images as RAW
images.</para>
<xi:include href="version-info.xml" xpointer="v220"/></listitem>
</varlistentry>
<varlistentry>
<term><command>list-transfers</command></term>
<listitem><para>Shows a list of container or VM image
downloads, imports and exports that are currently in
progress.</para>
<xi:include href="version-info.xml" xpointer="v219"/></listitem>
</varlistentry>
<varlistentry>
<term><command>cancel-transfer</command> <replaceable>ID</replaceable></term>
<listitem><para>Aborts a download, import or export of the
container or VM image with the specified ID. To list ongoing
transfers and their IDs, use
<command>list-transfers</command>. </para>
<xi:include href="version-info.xml" xpointer="v219"/></listitem>
</varlistentry>
</variablelist></refsect2>
</refsect1>
<refsect1>
@ -834,8 +648,7 @@
<listitem><para>When used with <command>bind</command>, creates a read-only bind mount.</para>
<para>When used with <command>clone</command>, <command>import-raw</command> or <command>import-tar</command> a
read-only container or VM image is created.</para>
<para>When used with <command>clone</command> a read-only container or VM image is created.</para>
<xi:include href="version-info.xml" xpointer="v219"/></listitem>
</varlistentry>
@ -866,27 +679,6 @@
<xi:include href="version-info.xml" xpointer="v219"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--verify=</option></term>
<listitem><para>When downloading a container or VM image,
specify whether the image shall be verified before it is made
available. Takes one of <literal>no</literal>,
<literal>checksum</literal> and <literal>signature</literal>.
If <literal>no</literal>, no verification is done. If
<literal>checksum</literal> is specified, the download is
checked for integrity after the transfer is complete, but no
signatures are verified. If <literal>signature</literal> is
specified, the checksum is verified and the image's signature
is checked against a local keyring of trustable vendors. It is
strongly recommended to set this option to
<literal>signature</literal> if the server and protocol
support this. Defaults to
<literal>signature</literal>.</para>
<xi:include href="version-info.xml" xpointer="v219"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--runner=</option><option>nspawn</option>|<option>vmspawn</option></term>
@ -926,28 +718,11 @@
<varlistentry>
<term><option>--force</option></term>
<listitem><para>When downloading a container or VM image, and
a local copy by the specified local machine name already
exists, delete it first and replace it by the newly downloaded
image.</para>
<listitem><para>Replace target file when copying files.</para>
<xi:include href="version-info.xml" xpointer="v219"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--format=</option></term>
<listitem><para>When used with the <option>export-tar</option>
or <option>export-raw</option> commands, specifies the
compression format to use for the resulting file. Takes one of
<literal>uncompressed</literal>, <literal>xz</literal>,
<literal>gzip</literal>, <literal>bzip2</literal>. By default,
the format is determined automatically from the image file
name passed.</para>
<xi:include href="version-info.xml" xpointer="v220"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--max-addresses=</option></term>
@ -1069,58 +844,8 @@
<refsect1>
<title>Examples</title>
<example>
<title>Download a Ubuntu image and open a shell in it</title>
<programlisting># machinectl pull-tar https://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-amd64-root.tar.gz
# systemd-nspawn -M trusty-server-cloudimg-amd64-root</programlisting>
<para>This downloads and verifies the specified
<filename>.tar</filename> image, and then uses
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>
to open a shell in it.</para>
</example>
<example>
<title>Download a Fedora image, set a root password in it, start
it as a service</title>
<programlisting># machinectl pull-raw --verify=no \
https://download.fedoraproject.org/pub/fedora/linux/releases/&fedora_latest_version;/Cloud/x86_64/images/Fedora-Cloud-Base-&fedora_latest_version;-&fedora_cloud_release;.x86_64.raw.xz \
Fedora-Cloud-Base-&fedora_latest_version;-&fedora_cloud_release;.x86-64
# systemd-nspawn -M Fedora-Cloud-Base-&fedora_latest_version;-&fedora_cloud_release;.x86-64
# passwd
# exit
# machinectl start Fedora-Cloud-Base-&fedora_latest_version;-&fedora_cloud_release;.x86-64
# machinectl login Fedora-Cloud-Base-&fedora_latest_version;-&fedora_cloud_release;.x86-64</programlisting>
<para>This downloads the specified <filename>.raw</filename>
image with verification disabled. Then, a shell is opened in it
and a root password is set. Afterwards the shell is left, and
the machine started as system service. With the last command a
login prompt into the container is requested.</para>
</example>
<example>
<title>Exports a container image as tar file</title>
<programlisting># machinectl export-tar fedora myfedora.tar.xz</programlisting>
<para>Exports the container <literal>fedora</literal> as an
xz-compressed tar file <filename>myfedora.tar.xz</filename> into the
current directory.</para>
</example>
<example>
<title>Create a new shell session</title>
<programlisting># machinectl shell --uid=lennart</programlisting>
<para>This creates a new shell session on the local host for
the user ID <literal>lennart</literal>, in a <citerefentry
project='die-net'><refentrytitle>su</refentrytitle><manvolnum>1</manvolnum></citerefentry>-like
fashion.</para>
</example>
<xi:include href="importctl.xml" xpointer="example-import-raw" />
</refsect1>
@ -1140,6 +865,7 @@
<member><citerefentry><refentrytitle>systemd-machined.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>importctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry project='die-net'><refentrytitle>tar</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry project='die-net'><refentrytitle>xz</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry project='die-net'><refentrytitle>gzip</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>

View file

@ -25,11 +25,11 @@
<para>
<citerefentry><refentrytitle>systemd-importd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
is a system service which may be used to import, export and download additional system images. These
images can be used by tools such as
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>
to run local containers. The service is used as the backend for <command>machinectl pull-raw</command>,
<command>machinectl pull-tar</command> and related commands. This page describes the D-Bus interface.
is a system service which may be used to import, export and download disk images. These images can be
used by tools such as
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry> to run
local containers. The service is used as the backend for <command>importctl pull-raw</command>,
<command>importctl pull-tar</command> and related commands. This page describes the D-Bus interface.
</para>
<para>Note that
@ -56,42 +56,94 @@ node /org/freedesktop/import1 {
in b read_only,
out u transfer_id,
out o transfer_path);
ImportTarEx(in h fd,
in s local_name,
in s class,
in t flags,
out u transfer_id,
out o transfer_path);
ImportRaw(in h fd,
in s local_name,
in b force,
in b read_only,
out u transfer_id,
out o transfer_path);
ImportRawEx(in h fd,
in s local_name,
in s class,
in t flags,
out u transfer_id,
out o transfer_path);
ImportFileSystem(in h fd,
in s local_name,
in b force,
in b read_only,
out u transfer_id,
out o transfer_path);
ImportFileSystemEx(in h fd,
in s local_name,
in s class,
in t flags,
out u transfer_id,
out o transfer_path);
ExportTar(in s local_name,
in h fd,
in s format,
out u transfer_id,
out o transfer_path);
ExportTarEx(in s local_name,
in s class,
in h fd,
in s format,
in t flags,
out u transfer_id,
out o transfer_path);
ExportRaw(in s local_name,
in h fd,
in s format,
out u transfer_id,
out o transfer_path);
ExportRawEx(in s local_name,
in s class,
in h fd,
in s format,
in t flags,
out u transfer_id,
out o transfer_path);
PullTar(in s url,
in s local_name,
in s verify_mode,
in b force,
out u transfer_id,
out o transfer_path);
PullTarEx(in s url,
in s local_name,
in s class,
in s verify_mode,
in t flags,
out u transfer_id,
out o transfer_path);
PullRaw(in s url,
in s local_name,
in s verify_mode,
in b force,
out u transfer_id,
out o transfer_path);
PullRawEx(in s url,
in s local_name,
in s class,
in s verify_mode,
in t flags,
out u transfer_id,
out o transfer_path);
ListTransfers(out a(usssdo) transfers);
ListTransfersEx(in s class,
in t flags,
out a(ussssdo) transfers);
CancelTransfer(in u transfer_id);
ListImages(in s class,
in t flags,
out a(ssssbtttttt) images);
signals:
TransferNew(u transfer_id,
o transfer_path);
@ -105,8 +157,6 @@ node /org/freedesktop/import1 {
};
</programlisting>
<!--method ImportFileSystem is not documented!-->
<!--Autogenerated cross-references for systemd.directives, do not edit-->
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.import1.Manager"/>
@ -115,22 +165,40 @@ node /org/freedesktop/import1 {
<variablelist class="dbus-method" generated="True" extra-ref="ImportTar()"/>
<variablelist class="dbus-method" generated="True" extra-ref="ImportTarEx()"/>
<variablelist class="dbus-method" generated="True" extra-ref="ImportRaw()"/>
<variablelist class="dbus-method" generated="True" extra-ref="ImportRawEx()"/>
<variablelist class="dbus-method" generated="True" extra-ref="ImportFileSystem()"/>
<variablelist class="dbus-method" generated="True" extra-ref="ImportFileSystemEx()"/>
<variablelist class="dbus-method" generated="True" extra-ref="ExportTar()"/>
<variablelist class="dbus-method" generated="True" extra-ref="ExportTarEx()"/>
<variablelist class="dbus-method" generated="True" extra-ref="ExportRaw()"/>
<variablelist class="dbus-method" generated="True" extra-ref="ExportRawEx()"/>
<variablelist class="dbus-method" generated="True" extra-ref="PullTar()"/>
<variablelist class="dbus-method" generated="True" extra-ref="PullTarEx()"/>
<variablelist class="dbus-method" generated="True" extra-ref="PullRaw()"/>
<variablelist class="dbus-method" generated="True" extra-ref="PullRawEx()"/>
<variablelist class="dbus-method" generated="True" extra-ref="ListTransfers()"/>
<variablelist class="dbus-method" generated="True" extra-ref="ListTransfersEx()"/>
<variablelist class="dbus-method" generated="True" extra-ref="CancelTransfer()"/>
<variablelist class="dbus-method" generated="True" extra-ref="ListImages()"/>
<variablelist class="dbus-signal" generated="True" extra-ref="TransferNew()"/>
<variablelist class="dbus-signal" generated="True" extra-ref="TransferRemoved()"/>
@ -140,81 +208,114 @@ node /org/freedesktop/import1 {
<refsect2>
<title>Methods</title>
<para><function>ImportTar()</function> and <function>ImportRaw()</function> import a system image and
place it into <filename>/var/lib/machines/</filename>. The first argument should be a file descriptor
(opened for reading) referring to the tar or raw file to import. It should reference a file on disk,
a pipe or a socket. When <function>ImportTar()</function> is used the file descriptor should
refer to a tar file, optionally compressed with
<citerefentry project="die-net"><refentrytitle>gzip</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry project="die-net"><refentrytitle>bzip2</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
or
<para><function>ImportTar()</function>/<function>ImportTarEx()</function> and
<function>ImportRaw()</function>/<function>ImportRawEx()</function> import a disk image and place it
into the image directory. The first argument should be a file descriptor (opened for reading) referring
to the tar or raw file to import. It should reference a file on disk, a pipe or a socket. When
<function>ImportTar()</function>/<function>ImportTarEx()</function> is used the file descriptor should
refer to a tar file, optionally compressed with <citerefentry project="die-net"><refentrytitle>gzip</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry project="die-net"><refentrytitle>bzip2</refentrytitle><manvolnum>1</manvolnum></citerefentry>, or
<citerefentry project="die-net"><refentrytitle>xz</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
<command>systemd-importd</command> will detect the used compression scheme (if any) automatically. When
<function>ImportRaw()</function> is used the file descriptor should refer to a raw or qcow2 disk image
containing an MBR or GPT disk label, also optionally compressed with gzip, bzip2 or xz. In either case,
if the file is specified as a file descriptor on disk, progress information is generated for the import
operation (as in that case we know the total size on disk). If a socket or pipe is specified, progress information is not
available. The file descriptor argument is followed by a local name for the image. This should be a
name suitable as a hostname and will be used to name the imported image below
<filename>/var/lib/machines/</filename>. A tar import is placed as a directory tree or a
<citerefentry project="url"><refentrytitle url="https://btrfs.readthedocs.io/en/latest/btrfs.html">btrfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>
subvolume below <filename>/var/lib/machines/</filename> under the specified name with no suffix
appended. A raw import is placed as a file in <filename>/var/lib/machines/</filename> with the
<filename>.raw</filename> suffix appended. If the <option>force</option> argument is true, any
pre-existing image with the same name is removed before starting the operation. Otherwise, the
operation fails if an image with the same name already exists. Finally, the
<option>read_only</option> argument controls
whether to create a writable or read-only image. Both methods return immediately after starting the import,
with the import transfer ongoing. They return a pair of transfer identifier and object path, which may
be used to retrieve progress information about the transfer or to cancel it. The transfer identifier is a
simple numeric identifier, the object path references an
<function>ImportRaw()</function>/<function>ImportRawEx()</function> is used the file descriptor should
refer to a raw or qcow2 disk image containing an MBR or GPT disk label, also optionally compressed with
gzip, bzip2 or xz. In either case, if the file is specified as a file descriptor on disk, progress
information is generated for the import operation (as in that case we know the total size on disk). If
a socket or pipe is specified, progress information is not available. The file descriptor argument is
followed by a local name for the image. This should be a name suitable as a hostname and will be used
to name the imported image below <filename>/var/lib/machines/</filename>. A tar import is placed as a
directory tree or a <citerefentry project="url"><refentrytitle url="https://btrfs.readthedocs.io/en/latest/btrfs.html">btrfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>
subvolume below the image directory under the specified name with no suffix appended. A raw import is
placed as a file in the image directory with the <filename>.raw</filename> suffix appended. In case of
<function>ImportTar()</function>/<function>ImportRaw()</function>, if the <option>force</option>
argument is true, any pre-existing image with the same name is removed before starting the
operation. Otherwise, the operation fails if an image with the same name already exists. The
<option>read_only</option> argument controls whether to create a writable or read-only image. In case
of <function>ImportTarEx()</function>/<function>ImportRawEx()</function> these boolean flags are
provided via a 64bit flags parameter instead, with bit 0 mapping to the <option>force</option>
parameter, and bit 1 mapping to <option>read_only</option>. The <option>class</option> parameter
specifies the image class, and takes one of <literal>machine</literal>, <literal>portable</literal>,
<literal>sysext</literal>, <literal>confext</literal>. All four methods return immediately after
starting the import, with the import transfer ongoing. They return a pair of transfer identifier and
object path, which may be used to retrieve progress information about the transfer or to cancel it. The
transfer identifier is a simple numeric identifier, the object path references an
<interfacename>org.freedesktop.import1.Transfer</interfacename> object, see below. Listen for a
<function>TransferRemoved()</function> signal for the transfer ID in order to detect when a transfer is
complete. The returned transfer object is useful to determine the current progress or log output of the
ongoing import operation.</para>
<para><function>ExportTar()</function> and <function>ExportRaw()</function> implement the reverse
operation, and may be used to export a system image in order to place it in a tar or raw image. They
take the machine name to export as their first parameter, followed by a file descriptor (opened for writing)
where the tar or raw file will be written. It may either reference a file on disk or a pipe/socket. The
third argument specifies in which compression format to write the image. It takes one of
<para><function>ExportTar()</function>/<function>ExportTarEx()</function> and
<function>ExportRaw()</function>/<function>ExportRaw()</function> implement the reverse operation, and
may be used to export a system image in order to place it in a tar or raw image. They take the machine
name to export as their first parameter, followed by a file descriptor (opened for writing) where the
tar or raw file will be written. It may either reference a file on disk or a pipe/socket. The third
argument specifies in which compression format to write the image. It takes one of
<literal>uncompressed</literal>, <literal>xz</literal>, <literal>bzip2</literal> or
<literal>gzip</literal>, depending on which compression scheme is required. The image written to the
specified file descriptor will be a tar file in case of <function>ExportTar()</function> or a raw disk
image in case of <function>ExportRaw()</function>. Note that currently raw disk images may not be
exported as tar files, and vice versa. This restriction might be lifted eventually. The method
returns a transfer identifier and object path for cancelling or tracking the export operation, similarly
to <function>ImportTar()</function> or <function>ImportRaw()</function> as described above.</para>
specified file descriptor will be a tar file in case of
<function>ExportTar()</function>/<function>ExportTarEx()</function> or a raw disk image in case of
<function>ExportRaw()</function>/<function>ExportRawEx()</function>. Note that currently raw disk
images may not be exported as tar files, and vice versa. This restriction might be lifted
eventually. The method returns a transfer identifier and object path for cancelling or tracking the
export operation, similarly to <function>ImportTar()</function>/<function>ImportTarEx()</function> or
<function>ImportRaw()</function>/<function>ImportRawEx()</function> as described
above. <function>ExportTarEx()</function>/<function>ExportRawEx()</function> expect the image class as
additional parameter, as well as a 64bit flags parameter that currently must be specified as
zero.</para>
<para><function>PullTar()</function> and <function>PullRaw()</function> may be used to download, verify
and import a system image from a URL. They take a URL argument which should point to a tar or
raw file on the <literal>http://</literal> or <literal>https://</literal> protocols, possibly
compressed with xz, bzip2 or gzip. The second argument is a local name for the image. It should be
suitable as a hostname, similarly to the matching argument of the <function>ImportTar()</function> and
<function>ImportRaw()</function> methods above. The third argument indicates the verification mode for
the image. It may be one of <literal>no</literal>, <literal>checksum</literal>,
<literal>signature</literal>. <literal>no</literal> turns off any kind of verification of the image;
<literal>checksum</literal> looks for a <filename>SHA256SUM</filename> file next to the downloaded
image and verifies any SHA256 hash value in that file against the image; <literal>signature</literal>
does the same but also tries to authenticate the <filename>SHA256SUM</filename> file via
<citerefentry project="man-pages"><refentrytitle>gpg</refentrytitle><manvolnum>8</manvolnum></citerefentry>
first. The last argument indicates whether to replace a possibly pre-existing image with the same local
name (if <literal>true</literal>), or whether to fail (if <literal>false</literal>). Like the import
and export calls above, these calls return a pair of transfer identifier and object path for the ongoing
<para><function>PullTar()</function>/<function>PullTarEx()</function> and
<function>PullRaw()</function>/<function>PullRawEx()</function> may be used to download, verify and
import a system image from a URL. They take a URL argument which should point to a tar or raw file on
the <literal>http://</literal> or <literal>https://</literal> protocols, possibly compressed with xz,
bzip2 or gzip. The second argument is a local name for the image. It should be suitable as a hostname,
similarly to the matching argument of the
<function>ImportTar()</function>/<function>ImportTarEx()</function> and
<function>ImportRaw()</function>/<function>ImportRawEx()</function> methods above. The third argument
indicates the verification mode for the image. It may be one of <literal>no</literal>,
<literal>checksum</literal>, <literal>signature</literal>. <literal>no</literal> turns off any kind of
verification of the image; <literal>checksum</literal> looks for a <filename>SHA256SUM</filename> file
next to the downloaded image and verifies any SHA256 hash value in that file against the image;
<literal>signature</literal> does the same but also tries to authenticate the
<filename>SHA256SUM</filename> file via <citerefentry project="man-pages"><refentrytitle>gpg</refentrytitle><manvolnum>8</manvolnum></citerefentry> first. In
case of <function>PullTar()</function>/<function>PullRaw()</function> the last argument indicates
whether to replace a possibly pre-existing image with the same local name (if <literal>true</literal>),
or whether to fail (if <literal>false</literal>). In case of
<function>PullTarEx()</function>/<function>PullRawEx()</function> the last argument is a 64bit flags
parameter, where bit 0 controls the <literal>force</literal> flag, bit 1 is a
<literal>read_only</literal> flag that controls whether the created image shall be marked read-only,
and bit 2 is a <literal>keep_download</literal> flag that indicates whether a pristine, read-only copy
of the downloaded image shell be kept, in addition for the local copy of the image. The
<function>…_Ex()</function> variants also expect an image class string (as above). Like the import and
export calls above, these calls return a pair of transfer identifier and object path for the ongoing
download.</para>
<para><function>ListTransfers()</function> returns a list of ongoing import, export or download
operations as created with the six calls described above. It returns an array of structures which
consist of the numeric transfer identifier, a string indicating the operation (one of
<literal>import-tar</literal>, <literal>import-raw</literal>, <literal>export-tar</literal>,
<literal>export-raw</literal>, <literal>pull-tar</literal> or <literal>pull-raw</literal>), a string
describing the remote file (in case of download operations this is the source URL, in case of
import/export operations this is a short string describing the file descriptor passed in), a string
with the local machine image name, a progress value between 0.0 (for 0%) and 1.0 (for 100%), as well as
the transfer object path.</para>
<para><function>ImportFileSystem()</function>/<function>ImportFileSystemEx()</function> are similar to
<function>ImportTar()</function>/<function>ImportTarEx()</function> but import a directory tree. The
first argument must refer to a directory file descriptor for the source hierarchy to import.</para>
<para><function>ListTransfers()</function>/<function>ListTransfersEx()</function> return a list of
ongoing import, export or download operations as created with the six calls described above. They
return an array of structures which consist of the numeric transfer identifier, a string indicating the
operation (one of <literal>import-tar</literal>, <literal>import-raw</literal>,
<literal>export-tar</literal>, <literal>export-raw</literal>, <literal>pull-tar</literal> or
<literal>pull-raw</literal>), a string describing the remote file (in case of download operations this
is the source URL, in case of import/export operations this is a short string describing the file
descriptor passed in), a string with the local machine image name, the image class (only in case of
<function>ListTransfersEx()</function>; one of <literal>machine</literal>, <literal>portable</literal>,
<literal>sysext</literal>, <literal>confext</literal>), a progress value between 0.0 (for 0%) and 1.0
(for 100%), as well as the transfer object path.</para>
<para><function>CancelTransfer()</function> may be used to cancel an ongoing import, export or download
operation. Simply specify the transfer identifier to cancel the ongoing operation.</para>
<para><function>ListImages()</function> returns a list of currently installed images. It takes a image
class string and a flags parameter. The image class is either the empty string or specifies one of the
four image classes, by which it will then filter. The flags parameter must be zero at this time. It
returns an array of items, each describing one image. The item fields are in order: the image class,
the local image name, the image type, the image path, the read-only flag, the creation and modification
times (in microseconds since the UNIX epoch), as well as the current disk usage in bytes (both overall,
and exclusive), as well as any size limit in bytes set on the image (both overall and
exclusive).</para>
</refsect2>
<refsect2>
@ -242,6 +343,7 @@ node /org/freedesktop/import1/transfer/_1 {
signals:
LogMessage(u priority,
s line);
ProgressUpdate(d progress);
properties:
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly u Id = ...;
@ -262,8 +364,6 @@ node /org/freedesktop/import1/transfer/_1 {
};
</programlisting>
<!--signal LogMessage is not documented!-->
<!--Autogenerated cross-references for systemd.directives, do not edit-->
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.import1.Transfer"/>
@ -274,6 +374,8 @@ node /org/freedesktop/import1/transfer/_1 {
<variablelist class="dbus-signal" generated="True" extra-ref="LogMessage()"/>
<variablelist class="dbus-signal" generated="True" extra-ref="ProgressUpdate()"/>
<variablelist class="dbus-property" generated="True" extra-ref="Id"/>
<variablelist class="dbus-property" generated="True" extra-ref="Local"/>
@ -315,6 +417,17 @@ node /org/freedesktop/import1/transfer/_1 {
between 0.0 and 1.0. To show a progress bar on screen we recommend to query this value in regular
intervals, for example every 500 ms or so.</para>
</refsect2>
<refsect2>
<title>Signals</title>
<para>The <function>LogMessage()</function> signal is emitted for log messages generated by a transfer. It
carries a pair of syslog log level integer and log string.</para>
<para>The <function>ProgressUpdate()</function> signal is emitted in regular intervals when new
download progress information is available for a transfer. It carries a double precision floating
pointer number between 0.0 and 1.0 indicating the transfer progress.</para>
</refsect2>
</refsect1>
<refsect1>
@ -340,4 +453,20 @@ node /org/freedesktop/import1/transfer/_1 {
</refsect1>
<xi:include href="org.freedesktop.locale1.xml" xpointer="versioning"/>
<refsect1>
<title>History</title>
<refsect2>
<title>The Manager Object</title>
<para><function>ImportTarEx()</function>, <function>ImportRawEx()</function>,
<function>ImportFileSystemEx()</function>, <function>ExportTarEx()</function>,
<function>ExportRawEx()</function>, <function>PullTarEx()</function>, <function>PullRawEx()</function>,
<function>ListTransfersEx()</function>, <function>ListImages()</function> were added in version
256.</para>
</refsect2>
<refsect2>
<title>Transfer Objects</title>
<para><function>ProgressUpdate()</function> was added in version 256.</para>
</refsect2>
</refsect1>
</refentry>

View file

@ -511,6 +511,7 @@
<member><citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>org.freedesktop.portable1</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-portabled.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>importctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>

View file

@ -23,6 +23,7 @@ manpages = [
['hostname', '5', [], ''],
['hostnamectl', '1', [], 'ENABLE_HOSTNAMED'],
['hwdb', '7', [], 'ENABLE_HWDB'],
['importctl', '1', [], 'ENABLE_MACHINED'],
['integritytab', '5', [], 'HAVE_LIBCRYPTSETUP'],
['iocost.conf', '5', [], ''],
['journal-remote.conf', '5', ['journal-remote.conf.d'], 'HAVE_MICROHTTPD'],

View file

@ -65,9 +65,11 @@
the exit code stored in the event loop object is updated, but
otherwise no further operation is executed.</para>
<para><function>sd_event_get_exit_code()</function> may be used to
query the exit code passed into
<function>sd_event_exit()</function> earlier.</para>
<para><function>sd_event_get_exit_code()</function> may be used to query the exit code passed to an
earlier call of <function>sd_event_exit()</function>. The return parameter <parameter>code</parameter>
may be set to <constant>NULL</constant>, in order to simply check if <function>sd_event_exit()</function>
has been called before (as <function>sd_event_get_exit_code()</function> fails with
<constant>-ENODATA</constant> if that's not the case, see below).</para>
<para>While the full positive and negative integer ranges may be used
for the exit code, care should be taken not pick exit codes that
@ -122,7 +124,8 @@
<varlistentry>
<term><constant>-ENODATA</constant></term>
<listitem><para>The event loop has not been requested to exit yet.</para></listitem>
<listitem><para>Returned by <function>sd_event_get_exit_code()</function> in case the event loop has not
been requested to exit yet.</para></listitem>
</varlistentry>
</variablelist>

View file

@ -29,12 +29,12 @@
<refsect1>
<title>Description</title>
<para><command>systemd-importd</command> is a system service that allows importing, exporting and downloading of
system images suitable for running as VM or containers. It is a companion service for
<citerefentry><refentrytitle>systemd-machined.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>, and provides the implementation for
<citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
<para><command>systemd-importd</command> is a system service that allows importing, exporting and
downloading of disk images. It provides the implementation for
<citerefentry><refentrytitle>importctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
<command>pull-raw</command>, <command>pull-tar</command>, <command>import-raw</command>,
<command>import-tar</command>, <command>import-fs</command>, <command>export-raw</command>, and <command>export-tar</command> commands.</para>
<command>import-tar</command>, <command>import-fs</command>, <command>export-raw</command>, and
<command>export-tar</command> commands.</para>
<para>See
<citerefentry><refentrytitle>org.freedesktop.import1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
@ -47,7 +47,7 @@
<title>See Also</title>
<para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>importctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-machined.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
</simplelist></para>

View file

@ -1800,19 +1800,7 @@ After=sys-subsystem-net-devices-ens1.device</programlisting>
<refsect1>
<title>Examples</title>
<example>
<title>Download a
<ulink url="https://getfedora.org">Fedora</ulink> image and start a shell in it</title>
<programlisting># machinectl pull-raw --verify=no \
https://download.fedoraproject.org/pub/fedora/linux/releases/&fedora_latest_version;/Cloud/x86_64/images/Fedora-Cloud-Base-&fedora_latest_version;-&fedora_cloud_release;.x86_64.raw.xz \
Fedora-Cloud-Base-&fedora_latest_version;-&fedora_cloud_release;.x86-64
# systemd-nspawn -M Fedora-Cloud-Base-&fedora_latest_version;-&fedora_cloud_release;.x86-64</programlisting>
<para>This downloads an image using
<citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
and opens a shell in it.</para>
</example>
<xi:include href="importctl.xml" xpointer="example-import-tar" />
<example>
<title>Build and boot a minimal Fedora distribution in a container</title>
@ -1921,6 +1909,7 @@ After=sys-subsystem-net-devices-ens1.device</programlisting>
<member><citerefentry project='mankier'><refentrytitle>zypper</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>importctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry project='url'><refentrytitle url='https://btrfs.readthedocs.io/en/latest/btrfs.html'>btrfs</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>

View file

@ -449,6 +449,7 @@
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>importctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>

View file

@ -485,6 +485,8 @@ $ systemd-vmspawn --image=image.raw
<para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>mkosi</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>importctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>
</refentry>

View file

@ -926,6 +926,8 @@ int fd_verify_safe_flags(int fd) {
*
* RAW_O_LARGEFILE: glibc secretly sets this and neglects to hide it from us if we call fcntl.
* See comment in missing_fcntl.h for more details about this.
*
* O_DIRECTORY: this is set for directories, which are totally fine
*/
assert(fd >= 0);
@ -934,7 +936,7 @@ int fd_verify_safe_flags(int fd) {
if (flags < 0)
return -errno;
unexpected_flags = flags & ~(O_ACCMODE|O_NOFOLLOW|RAW_O_LARGEFILE);
unexpected_flags = flags & ~(O_ACCMODE|O_NOFOLLOW|RAW_O_LARGEFILE|O_DIRECTORY);
if (unexpected_flags != 0)
return log_debug_errno(SYNTHETIC_ERRNO(EREMOTEIO),
"Unexpected flags set for extrinsic fd: 0%o",

View file

@ -41,6 +41,8 @@ const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
[SPECIAL_GLYPH_TREE_SPACE] = " ",
[SPECIAL_GLYPH_TREE_TOP] = ",-",
[SPECIAL_GLYPH_VERTICAL_DOTTED] = ":",
[SPECIAL_GLYPH_HORIZONTAL_DOTTED] = "-",
[SPECIAL_GLYPH_HORIZONTAL_FAT] = "=",
[SPECIAL_GLYPH_TRIANGULAR_BULLET] = ">",
[SPECIAL_GLYPH_BLACK_CIRCLE] = "*",
[SPECIAL_GLYPH_WHITE_CIRCLE] = "*",
@ -91,6 +93,8 @@ const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
/* Single glyphs in both cases */
[SPECIAL_GLYPH_VERTICAL_DOTTED] = u8"",
[SPECIAL_GLYPH_HORIZONTAL_DOTTED] = u8"",
[SPECIAL_GLYPH_HORIZONTAL_FAT] = u8"",
[SPECIAL_GLYPH_TRIANGULAR_BULLET] = u8"",
[SPECIAL_GLYPH_BLACK_CIRCLE] = u8"",
[SPECIAL_GLYPH_WHITE_CIRCLE] = u8"",

View file

@ -13,6 +13,8 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_TREE_SPACE,
SPECIAL_GLYPH_TREE_TOP,
SPECIAL_GLYPH_VERTICAL_DOTTED,
SPECIAL_GLYPH_HORIZONTAL_DOTTED,
SPECIAL_GLYPH_HORIZONTAL_FAT,
SPECIAL_GLYPH_TRIANGULAR_BULLET,
SPECIAL_GLYPH_BLACK_CIRCLE,
SPECIAL_GLYPH_WHITE_CIRCLE,

View file

@ -10,20 +10,28 @@
#include "version.h"
static void curl_glue_check_finished(CurlGlue *g) {
CURLMsg *msg;
int k = 0;
int r;
assert(g);
/* sd_event_get_exit_code() returns -ENODATA if no exit was scheduled yet */
r = sd_event_get_exit_code(g->event, /* ret_code= */ NULL);
if (r >= 0)
return; /* exit scheduled? Then don't process this anymore */
if (r != -ENODATA)
log_debug_errno(r, "Unexpected error while checking for event loop exit code, ignoring: %m");
CURLMsg *msg;
int k = 0;
msg = curl_multi_info_read(g->curl, &k);
if (!msg)
return;
if (msg->msg != CURLMSG_DONE)
return;
if (g->on_finished)
if (msg->msg == CURLMSG_DONE && g->on_finished)
g->on_finished(g, msg->easy_handle, msg->data.result);
/* This is a queue, process another item soon, but do so in a later event loop iteration. */
(void) sd_event_source_set_enabled(g->defer, SD_EVENT_ONESHOT);
}
static int curl_glue_on_io(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
@ -153,6 +161,15 @@ static int curl_glue_timer_callback(CURLM *curl, long timeout_ms, void *userdata
return 0;
}
static int curl_glue_on_defer(sd_event_source *s, void *userdata) {
CurlGlue *g = ASSERT_PTR(userdata);
assert(s);
curl_glue_check_finished(g);
return 0;
}
CurlGlue *curl_glue_unref(CurlGlue *g) {
sd_event_source *io;
@ -167,7 +184,8 @@ CurlGlue *curl_glue_unref(CurlGlue *g) {
hashmap_free(g->ios);
sd_event_source_unref(g->timer);
sd_event_source_disable_unref(g->timer);
sd_event_source_disable_unref(g->defer);
sd_event_unref(g->event);
return mfree(g);
}
@ -211,6 +229,12 @@ int curl_glue_new(CurlGlue **glue, sd_event *event) {
if (curl_multi_setopt(g->curl, CURLMOPT_TIMERFUNCTION, curl_glue_timer_callback) != CURLM_OK)
return -EINVAL;
r = sd_event_add_defer(g->event, &g->defer, curl_glue_on_defer, g);
if (r < 0)
return r;
(void) sd_event_source_set_description(g->defer, "curl-defer");
*glue = TAKE_PTR(g);
return 0;

View file

@ -16,6 +16,7 @@ struct CurlGlue {
CURLM *curl;
sd_event_source *timer;
Hashmap *ios;
sd_event_source *defer;
void (*on_finished)(CurlGlue *g, CURL *curl, CURLcode code);
void *userdata;

View file

@ -14,6 +14,7 @@
#include "fd-util.h"
#include "fs-util.h"
#include "hostname-util.h"
#include "import-common.h"
#include "import-util.h"
#include "main-func.h"
#include "signal-util.h"
@ -22,6 +23,7 @@
#include "verbs.h"
static ImportCompressType arg_compress = IMPORT_COMPRESS_UNKNOWN;
static ImageClass arg_class = IMAGE_MACHINE;
static void determine_compression_from_filename(const char *p) {
@ -43,12 +45,6 @@ static void determine_compression_from_filename(const char *p) {
arg_compress = IMPORT_COMPRESS_UNCOMPRESSED;
}
static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
log_notice("Transfer aborted.");
sd_event_exit(sd_event_source_get_event(s), EINTR);
return 0;
}
static void on_tar_finished(TarExport *export, int error, void *userdata) {
sd_event *event = userdata;
assert(export);
@ -67,12 +63,13 @@ static int export_tar(int argc, char *argv[], void *userdata) {
_cleanup_close_ int open_fd = -EBADF;
int r, fd;
if (hostname_is_valid(argv[1], 0)) {
r = image_find(IMAGE_MACHINE, argv[1], NULL, &image);
local = argv[1];
if (image_name_is_valid(local)) {
r = image_find(arg_class, local, NULL, &image);
if (r == -ENOENT)
return log_error_errno(r, "Machine image %s not found.", argv[1]);
return log_error_errno(r, "Image %s not found.", local);
if (r < 0)
return log_error_errno(r, "Failed to look for machine %s: %m", argv[1]);
return log_error_errno(r, "Failed to look for image %s: %m", local);
local = image->path;
} else
@ -101,13 +98,9 @@ static int export_tar(int argc, char *argv[], void *userdata) {
log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, strna(pretty), import_compress_type_to_string(arg_compress));
}
r = sd_event_default(&event);
r = import_allocate_event_with_signals(&event);
if (r < 0)
return log_error_errno(r, "Failed to allocate event loop: %m");
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT) >= 0);
(void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
(void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
return r;
r = tar_export_new(&export, event, on_tar_finished, event);
if (r < 0)
@ -143,12 +136,13 @@ static int export_raw(int argc, char *argv[], void *userdata) {
_cleanup_close_ int open_fd = -EBADF;
int r, fd;
if (hostname_is_valid(argv[1], 0)) {
r = image_find(IMAGE_MACHINE, argv[1], NULL, &image);
local = argv[1];
if (image_name_is_valid(local)) {
r = image_find(arg_class, local, NULL, &image);
if (r == -ENOENT)
return log_error_errno(r, "Machine image %s not found.", argv[1]);
return log_error_errno(r, "Image %s not found.", local);
if (r < 0)
return log_error_errno(r, "Failed to look for machine %s: %m", argv[1]);
return log_error_errno(r, "Failed to look for image %s: %m", local);
local = image->path;
} else
@ -177,13 +171,9 @@ static int export_raw(int argc, char *argv[], void *userdata) {
log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, strna(pretty), import_compress_type_to_string(arg_compress));
}
r = sd_event_default(&event);
r = import_allocate_event_with_signals(&event);
if (r < 0)
return log_error_errno(r, "Failed to allocate event loop: %m");
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT) >= 0);
(void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
(void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
return r;
r = raw_export_new(&export, event, on_raw_finished, event);
if (r < 0)
@ -203,14 +193,16 @@ static int export_raw(int argc, char *argv[], void *userdata) {
static int help(int argc, char *argv[], void *userdata) {
printf("%1$s [OPTIONS...] {COMMAND} ...\n"
"\n%4$sExport container or virtual machine images.%5$s\n"
"\n%4$sExport disk images.%5$s\n"
"\n%2$sCommands:%3$s\n"
" tar NAME [FILE] Export a TAR image\n"
" raw NAME [FILE] Export a RAW image\n"
"\n%2$sOptions:%3$s\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --format=FORMAT Select format\n\n",
" --format=FORMAT Select format\n"
" --class=CLASS Select image class (machine, sysext, confext,\n"
" portable)\n",
program_invocation_short_name,
ansi_underline(),
ansi_normal(),
@ -225,12 +217,14 @@ static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_FORMAT,
ARG_CLASS,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "format", required_argument, NULL, ARG_FORMAT },
{ "class", required_argument, NULL, ARG_CLASS },
{}
};
@ -263,6 +257,13 @@ static int parse_argv(int argc, char *argv[]) {
"Unknown format: %s", optarg);
break;
case ARG_CLASS:
arg_class = image_class_from_string(optarg);
if (arg_class < 0)
return log_error_errno(arg_class, "Failed to parse --class= argument: %s", optarg);
break;
case '?':
return -EINVAL;
@ -288,8 +289,7 @@ static int run(int argc, char *argv[]) {
int r;
setlocale(LC_ALL, "");
log_parse_environment();
log_open();
log_setup();
r = parse_argv(argc, argv);
if (r <= 0)

View file

@ -276,7 +276,7 @@ bool import_validate_local(const char *name, ImportFlags flags) {
if (FLAGS_SET(flags, IMPORT_DIRECT))
return path_is_valid(name);
return hostname_is_valid(name, 0);
return image_name_is_valid(name);
}
static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
@ -295,9 +295,8 @@ int import_allocate_event_with_signals(sd_event **ret) {
if (r < 0)
return log_error_errno(r, "Failed to allocate event loop: %m");
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT) >= 0);
(void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
(void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
(void) sd_event_add_signal(event, NULL, SIGTERM|SD_EVENT_SIGNAL_PROCMASK, interrupt_signal_handler, NULL);
(void) sd_event_add_signal(event, NULL, SIGINT|SD_EVENT_SIGNAL_PROCMASK, interrupt_signal_handler, NULL);
*ret = TAKE_PTR(event);
return 0;

View file

@ -6,16 +6,33 @@
#include "sd-event.h"
typedef enum ImportFlags {
IMPORT_FORCE = 1 << 0, /* replace existing image */
IMPORT_READ_ONLY = 1 << 1, /* make generated image read-only */
IMPORT_BTRFS_SUBVOL = 1 << 2, /* tar: preferably create images as btrfs subvols */
IMPORT_BTRFS_QUOTA = 1 << 3, /* tar: set up btrfs quota for new subvolume as child of parent subvolume */
IMPORT_CONVERT_QCOW2 = 1 << 4, /* raw: if we detect a qcow2 image, unpack it */
IMPORT_DIRECT = 1 << 5, /* import without rename games */
IMPORT_SYNC = 1 << 6, /* fsync() right before we are done */
/* Public Flags (i.e. accessible via D-Bus, must stay stable! */
IMPORT_FORCE = 1 << 0, /* replace existing image */
IMPORT_READ_ONLY = 1 << 1, /* make generated image read-only */
IMPORT_PULL_KEEP_DOWNLOAD = 1 << 2, /* keep a pristine copy of the downloaded file around */
IMPORT_FLAGS_MASK_TAR = IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_BTRFS_SUBVOL|IMPORT_BTRFS_QUOTA|IMPORT_DIRECT|IMPORT_SYNC,
IMPORT_FLAGS_MASK_RAW = IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_CONVERT_QCOW2|IMPORT_DIRECT|IMPORT_SYNC,
/* Private flags */
IMPORT_BTRFS_SUBVOL = 1 << 3, /* tar: preferably create images as btrfs subvols */
IMPORT_BTRFS_QUOTA = 1 << 4, /* tar: set up btrfs quota for new subvolume as child of parent subvolume */
IMPORT_CONVERT_QCOW2 = 1 << 5, /* raw: if we detect a qcow2 image, unpack it */
IMPORT_DIRECT = 1 << 6, /* import without rename games */
IMPORT_SYNC = 1 << 7, /* fsync() right before we are done */
/* When pulling these flags are defined too */
IMPORT_PULL_SETTINGS = 1 << 8, /* download .nspawn settings file */
IMPORT_PULL_ROOTHASH = 1 << 9, /* only for raw: download .roothash file for verity */
IMPORT_PULL_ROOTHASH_SIGNATURE = 1 << 10, /* only for raw: download .roothash.p7s file for verity */
IMPORT_PULL_VERITY = 1 << 11, /* only for raw: download .verity file for verity */
/* The supported flags for the tar and the raw importing */
IMPORT_FLAGS_MASK_TAR = IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_BTRFS_SUBVOL|IMPORT_BTRFS_QUOTA|IMPORT_DIRECT|IMPORT_SYNC,
IMPORT_FLAGS_MASK_RAW = IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_CONVERT_QCOW2|IMPORT_DIRECT|IMPORT_SYNC,
/* The supported flags for the tar and the raw pulling */
IMPORT_PULL_FLAGS_MASK_TAR = IMPORT_FLAGS_MASK_TAR|IMPORT_PULL_KEEP_DOWNLOAD|IMPORT_PULL_SETTINGS,
IMPORT_PULL_FLAGS_MASK_RAW = IMPORT_FLAGS_MASK_RAW|IMPORT_PULL_KEEP_DOWNLOAD|IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY,
_IMPORT_FLAGS_INVALID = -EINVAL,
} ImportFlags;
int import_fork_tar_c(const char *path, pid_t *ret);

View file

@ -31,7 +31,8 @@ static bool arg_btrfs_subvol = true;
static bool arg_btrfs_quota = true;
static bool arg_sync = true;
static bool arg_direct = false;
static const char *arg_image_root = "/var/lib/machines";
static const char *arg_image_root = NULL;
static ImageClass arg_class = IMAGE_MACHINE;
typedef struct ProgressInfo {
RateLimit limit;
@ -132,7 +133,7 @@ static int import_fs(int argc, char *argv[], void *userdata) {
"Local path name '%s' is not valid.", final_path);
} else {
if (local) {
if (!hostname_is_valid(local, 0))
if (!image_name_is_valid(local))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local image name '%s' is not valid.", local);
} else
@ -143,7 +144,7 @@ static int import_fs(int argc, char *argv[], void *userdata) {
return log_oom();
if (!arg_force) {
r = image_find(IMAGE_MACHINE, local, NULL, NULL);
r = image_find(arg_class, local, NULL, NULL);
if (r < 0) {
if (r != -ENOENT)
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
@ -170,6 +171,8 @@ static int import_fs(int argc, char *argv[], void *userdata) {
log_info("Importing '%s', saving as '%s'.", strempty(pretty), local);
}
log_info("Operating on image directory '%s'.", arg_image_root);
if (!arg_sync)
log_info("File system synchronization on completion is off.");
@ -266,7 +269,9 @@ static int help(int argc, char *argv[], void *userdata) {
" instead of a directory\n"
" --btrfs-quota=BOOL Controls whether to set up quota for btrfs\n"
" subvolume\n"
" --sync=BOOL Controls whether to sync() before completing\n",
" --sync=BOOL Controls whether to sync() before completing\n"
" --class=CLASS Select image class (machine, sysext, confext,\n"
" portable)\n",
program_invocation_short_name,
ansi_underline(),
ansi_normal(),
@ -287,6 +292,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_BTRFS_SUBVOL,
ARG_BTRFS_QUOTA,
ARG_SYNC,
ARG_CLASS,
};
static const struct option options[] = {
@ -299,6 +305,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "btrfs-subvol", required_argument, NULL, ARG_BTRFS_SUBVOL },
{ "btrfs-quota", required_argument, NULL, ARG_BTRFS_QUOTA },
{ "sync", required_argument, NULL, ARG_SYNC },
{ "class", required_argument, NULL, ARG_CLASS },
{}
};
@ -354,6 +361,13 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_CLASS:
arg_class = image_class_from_string(optarg);
if (arg_class < 0)
return log_error_errno(arg_class, "Failed to parse --class= argument: %s", optarg);
break;
case '?':
return -EINVAL;
@ -361,6 +375,9 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached();
}
if (!arg_image_root)
arg_image_root = image_root_to_string(arg_class);
return 1;
}
@ -379,8 +396,7 @@ static int run(int argc, char *argv[]) {
int r;
setlocale(LC_ALL, "");
log_parse_environment();
log_open();
log_setup();
r = parse_argv(argc, argv);
if (r <= 0)

View file

@ -95,8 +95,9 @@ int raw_import_new(
int r;
assert(ret);
assert(image_root);
root = strdup(image_root ?: "/var/lib/machines");
root = strdup(image_root);
if (!root)
return -ENOMEM;

View file

@ -97,8 +97,9 @@ int tar_import_new(
int r;
assert(ret);
assert(image_root);
root = strdup(image_root ?: "/var/lib/machines");
root = strdup(image_root);
if (!root)
return -ENOMEM;

View file

@ -25,9 +25,10 @@
#include "terminal-util.h"
#include "verbs.h"
static const char *arg_image_root = "/var/lib/machines";
static const char *arg_image_root = NULL;
static ImportFlags arg_import_flags = IMPORT_BTRFS_SUBVOL | IMPORT_BTRFS_QUOTA | IMPORT_CONVERT_QCOW2 | IMPORT_SYNC;
static uint64_t arg_offset = UINT64_MAX, arg_size_max = UINT64_MAX;
static ImageClass arg_class = IMAGE_MACHINE;
static int normalize_local(const char *local, char **ret) {
_cleanup_free_ char *ll = NULL;
@ -53,7 +54,7 @@ static int normalize_local(const char *local, char **ret) {
"Local path name '%s' is not valid.", local);
} else {
if (local) {
if (!hostname_is_valid(local, 0))
if (!image_name_is_valid(local))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local image name '%s' is not valid.",
local);
@ -61,7 +62,7 @@ static int normalize_local(const char *local, char **ret) {
local = "imported";
if (!FLAGS_SET(arg_import_flags, IMPORT_FORCE)) {
r = image_find(IMAGE_MACHINE, local, NULL, NULL);
r = image_find(arg_class, local, NULL, NULL);
if (r < 0) {
if (r != -ENOENT)
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
@ -113,6 +114,12 @@ static int open_source(const char *path, const char *local, int *ret_open_fd) {
log_info("Importing '%s', saving as '%s'.", strempty(pretty), local);
}
if (!FLAGS_SET(arg_import_flags, IMPORT_DIRECT))
log_info("Operating on image directory '%s'.", arg_image_root);
if (!FLAGS_SET(arg_import_flags, IMPORT_SYNC))
log_info("File system synchronization on completion is off.");
*ret_open_fd = TAKE_FD(open_fd);
return retval;
}
@ -160,15 +167,12 @@ static int import_tar(int argc, char *argv[], void *userdata) {
fd = open_source(path, normalized, &open_fd);
if (fd < 0)
return r;
return fd;
r = import_allocate_event_with_signals(&event);
if (r < 0)
return r;
if (!FLAGS_SET(arg_import_flags, IMPORT_SYNC))
log_info("File system synchronization on completion is off.");
r = tar_import_new(&import, event, arg_image_root, on_tar_finished, event);
if (r < 0)
return log_error_errno(r, "Failed to allocate importer: %m");
@ -238,9 +242,6 @@ static int import_raw(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
if (!FLAGS_SET(arg_import_flags, IMPORT_SYNC))
log_info("File system synchronization on completion is off.");
r = raw_import_new(&import, event, arg_image_root, on_raw_finished, event);
if (r < 0)
return log_error_errno(r, "Failed to allocate importer: %m");
@ -266,7 +267,7 @@ static int import_raw(int argc, char *argv[], void *userdata) {
static int help(int argc, char *argv[], void *userdata) {
printf("%1$s [OPTIONS...] {COMMAND} ...\n"
"\n%4$sImport container or virtual machine images.%5$s\n"
"\n%4$sImport disk images.%5$s\n"
"\n%2$sCommands:%3$s\n"
" tar FILE [NAME] Import a TAR image\n"
" raw FILE [NAME] Import a RAW image\n"
@ -285,7 +286,9 @@ static int help(int argc, char *argv[], void *userdata) {
" regular disk images\n"
" --sync=BOOL Controls whether to sync() before completing\n"
" --offset=BYTES Offset to seek to in destination\n"
" --size-max=BYTES Maximum number of bytes to write to destination\n",
" --size-max=BYTES Maximum number of bytes to write to destination\n"
" --class=CLASS Select image class (machine, sysext, confext,\n"
" portable)\n",
program_invocation_short_name,
ansi_underline(),
ansi_normal(),
@ -309,6 +312,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_SYNC,
ARG_OFFSET,
ARG_SIZE_MAX,
ARG_CLASS,
};
static const struct option options[] = {
@ -324,6 +328,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "sync", required_argument, NULL, ARG_SYNC },
{ "offset", required_argument, NULL, ARG_OFFSET },
{ "size-max", required_argument, NULL, ARG_SIZE_MAX },
{ "class", required_argument, NULL, ARG_CLASS },
{}
};
@ -416,6 +421,13 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
case ARG_CLASS:
arg_class = image_class_from_string(optarg);
if (arg_class < 0)
return log_error_errno(arg_class, "Failed to parse --class= argument: %s", optarg);
break;
case '?':
return -EINVAL;
@ -432,6 +444,9 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_offset != UINT64_MAX && !FLAGS_SET(arg_import_flags, IMPORT_DIRECT))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File offset only supported in --direct mode.");
if (!arg_image_root)
arg_image_root = image_root_to_string(arg_class);
return 1;
}
@ -475,8 +490,7 @@ static int run(int argc, char *argv[]) {
int r;
setlocale(LC_ALL, "");
log_parse_environment();
log_open();
log_setup();
parse_env();

1245
src/import/importctl.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -14,15 +14,19 @@
#include "common-signal.h"
#include "constants.h"
#include "daemon-util.h"
#include "discover-image.h"
#include "env-util.h"
#include "event-util.h"
#include "fd-util.h"
#include "float.h"
#include "hostname-util.h"
#include "import-common.h"
#include "import-util.h"
#include "machine-pool.h"
#include "main-func.h"
#include "missing_capability.h"
#include "mkdir-label.h"
#include "os-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "percent-util.h"
@ -63,12 +67,11 @@ struct Transfer {
char *remote;
char *local;
bool force_local;
bool read_only;
ImageClass class;
ImportFlags flags;
char *format;
pid_t pid;
PidRef pidref;
int log_fd;
@ -80,6 +83,7 @@ struct Transfer {
unsigned n_canceled;
unsigned progress_percent;
unsigned progress_percent_sent;
int stdin_fd;
int stdout_fd;
@ -107,11 +111,11 @@ struct Manager {
static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = {
[TRANSFER_IMPORT_TAR] = "import-tar",
[TRANSFER_IMPORT_RAW] = "import-raw",
[TRANSFER_IMPORT_FS] = "import-fs",
[TRANSFER_IMPORT_FS] = "import-fs",
[TRANSFER_EXPORT_TAR] = "export-tar",
[TRANSFER_EXPORT_RAW] = "export-raw",
[TRANSFER_PULL_TAR] = "pull-tar",
[TRANSFER_PULL_RAW] = "pull-raw",
[TRANSFER_PULL_TAR] = "pull-tar",
[TRANSFER_PULL_RAW] = "pull-raw",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type, TransferType);
@ -131,8 +135,7 @@ static Transfer *transfer_unref(Transfer *t) {
free(t->format);
free(t->object_path);
if (t->pid > 1)
sigkill_wait(t->pid);
pidref_done_sigkill_wait(&t->pidref);
safe_close(t->log_fd);
safe_close(t->stdin_fd);
@ -164,7 +167,8 @@ static int transfer_new(Manager *m, Transfer **ret) {
.stdin_fd = -EBADF,
.stdout_fd = -EBADF,
.verify = _IMPORT_VERIFY_INVALID,
.progress_percent= UINT_MAX,
.progress_percent = UINT_MAX,
.progress_percent_sent = UINT_MAX,
};
id = m->current_transfer_id + 1;
@ -215,7 +219,28 @@ static void transfer_send_log_line(Transfer *t, const char *line) {
line);
if (r < 0)
log_warning_errno(r, "Cannot emit log message signal, ignoring: %m");
}
}
static void transfer_send_progress_update(Transfer *t) {
int r;
assert(t);
if (t->progress_percent_sent == t->progress_percent)
return;
r = sd_bus_emit_signal(
t->manager->bus,
t->object_path,
"org.freedesktop.import1.Transfer",
"ProgressUpdate",
"d",
transfer_percent_as_double(t));
if (r < 0)
log_warning_errno(r, "Cannot emit progress update signal, ignoring: %m");
t->progress_percent_sent = t->progress_percent;
}
static void transfer_send_logs(Transfer *t, bool flush) {
assert(t);
@ -302,7 +327,7 @@ static int transfer_cancel(Transfer *t) {
assert(t);
r = kill_and_sigcont(t->pid, t->n_canceled < 3 ? SIGTERM : SIGKILL);
r = pidref_kill_and_sigcont(&t->pidref, t->n_canceled < 3 ? SIGTERM : SIGKILL);
if (r < 0)
return r;
@ -329,7 +354,7 @@ static int transfer_on_pid(sd_event_source *s, const siginfo_t *si, void *userda
else
log_error("Transfer process failed due to unknown reason.");
t->pid = 0;
pidref_done(&t->pidref);
return transfer_finalize(t, success);
}
@ -363,15 +388,17 @@ static int transfer_start(Transfer *t) {
int r;
assert(t);
assert(t->pid <= 0);
assert(!pidref_is_set(&t->pidref));
if (pipe2(pipefd, O_CLOEXEC) < 0)
return -errno;
r = safe_fork_full("(sd-transfer)",
(int[]) { t->stdin_fd, t->stdout_fd < 0 ? pipefd[1] : t->stdout_fd, pipefd[1] },
NULL, 0,
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO, &t->pid);
r = pidref_safe_fork_full(
"(sd-transfer)",
(int[]) { t->stdin_fd, t->stdout_fd < 0 ? pipefd[1] : t->stdout_fd, pipefd[1] },
/* except_fds= */ NULL, /* n_except_fds= */ 0,
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_REOPEN_LOG,
&t->pidref);
if (r < 0)
return r;
if (r == 0) {
@ -380,6 +407,9 @@ static int transfer_start(Transfer *t) {
NULL, /* tar, raw */
NULL, /* --verify= */
NULL, /* verify argument */
NULL, /* --class= */
NULL, /* class argument */
NULL, /* --keep-download= */
NULL, /* maybe --force */
NULL, /* maybe --read-only */
NULL, /* if so: the actual URL */
@ -389,7 +419,7 @@ static int transfer_start(Transfer *t) {
NULL, /* local */
NULL
};
unsigned k = 0;
size_t k = 0;
/* Child */
@ -399,6 +429,10 @@ static int transfer_start(Transfer *t) {
_exit(EXIT_FAILURE);
}
r = setenv_systemd_log_level();
if (r < 0)
log_warning_errno(r, "Failed to update $SYSTEMD_LOG_LEVEL, ignoring: %m");
r = setenv_systemd_exec_pid(true);
if (r < 0)
log_warning_errno(r, "Failed to update $SYSTEMD_EXEC_PID, ignoring: %m");
@ -455,9 +489,18 @@ static int transfer_start(Transfer *t) {
cmd[k++] = import_verify_to_string(t->verify);
}
if (t->force_local)
if (t->class != IMAGE_MACHINE) {
cmd[k++] = "--class";
cmd[k++] = image_class_to_string(t->class);
}
if (IN_SET(t->type, TRANSFER_PULL_TAR, TRANSFER_PULL_RAW))
cmd[k++] = FLAGS_SET(t->flags, IMPORT_PULL_KEEP_DOWNLOAD) ?
"--keep-download=yes" : "--keep-download=no";
if (FLAGS_SET(t->flags, IMPORT_FORCE))
cmd[k++] = "--force";
if (t->read_only)
if (FLAGS_SET(t->flags, IMPORT_READ_ONLY))
cmd[k++] = "--read-only";
if (t->format) {
@ -478,6 +521,11 @@ static int transfer_start(Transfer *t) {
assert(k < ELEMENTSOF(cmd));
if (DEBUG_LOGGING) {
_cleanup_free_ char *joined = strv_join((char**) cmd, " ");
log_debug("Calling: %s", strnull(joined));
}
r = invoke_callout_binary(cmd[0], (char * const *) cmd);
log_error_errno(r, "Failed to execute %s tool: %m", cmd[0]);
_exit(EXIT_FAILURE);
@ -488,8 +536,13 @@ static int transfer_start(Transfer *t) {
t->stdin_fd = safe_close(t->stdin_fd);
r = sd_event_add_child(t->manager->event, &t->pid_event_source,
t->pid, WEXITED, transfer_on_pid, t);
r = event_add_child_pidref(
t->manager->event,
&t->pid_event_source,
&t->pidref,
WEXITED,
transfer_on_pid,
t);
if (r < 0)
return r;
@ -584,7 +637,7 @@ static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void
}
HASHMAP_FOREACH(t, m->transfers)
if (ucred->pid == t->pid)
if (ucred->pid == t->pidref.pid)
break;
if (!t) {
@ -609,6 +662,8 @@ static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void
t->progress_percent = (unsigned) r;
log_debug("Got percentage from client: %u%%", t->progress_percent);
transfer_send_progress_update(t);
return 0;
}
@ -635,17 +690,11 @@ static int manager_new(Manager **ret) {
if (r < 0)
return r;
(void) sd_event_set_watchdog(m->event, true);
r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
r = sd_event_set_signal_exit(m->event, true);
if (r < 0)
return r;
r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
if (r < 0)
return r;
r = sd_event_add_signal(m->event, NULL, SIGRTMIN+18, sigrtmin18_handler, NULL);
r = sd_event_add_signal(m->event, NULL, (SIGRTMIN+18)|SD_EVENT_SIGNAL_PROCMASK, sigrtmin18_handler, NULL);
if (r < 0)
return r;
@ -653,6 +702,10 @@ static int manager_new(Manager **ret) {
if (r < 0)
log_debug_errno(r, "Failed allocate memory pressure event source, ignoring: %m");
r = sd_event_set_watchdog(m->event, true);
if (r < 0)
log_debug_errno(r, "Failed to enable watchdog logic, ignoring: %m");
r = sd_bus_default_system(&m->bus);
if (r < 0)
return r;
@ -697,12 +750,13 @@ static Transfer *manager_find(Manager *m, TransferType type, const char *remote)
static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
_cleanup_(transfer_unrefp) Transfer *t = NULL;
int fd, force, read_only, r;
const char *local, *object;
ImageClass class = _IMAGE_CLASS_INVALID;
Manager *m = ASSERT_PTR(userdata);
const char *local;
TransferType type;
struct stat st;
uint32_t id;
uint64_t flags;
int fd, r;
assert(msg);
@ -717,7 +771,36 @@ static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
if (r == 0)
return 1; /* Will call us back */
r = sd_bus_message_read(msg, "hsbb", &fd, &local, &force, &read_only);
if (endswith(sd_bus_message_get_member(msg), "Ex")) {
const char *sclass;
r = sd_bus_message_read(msg, "hsst", &fd, &local, &sclass, &flags);
if (r < 0)
return r;
class = image_class_from_string(sclass);
if (class < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Image class '%s' not known", sclass);
if (flags & ~(IMPORT_READ_ONLY|IMPORT_FORCE))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Flags 0x%" PRIx64 " invalid", flags);
} else {
int force, read_only;
r = sd_bus_message_read(msg, "hsbb", &fd, &local, &force, &read_only);
if (r < 0)
return r;
class = IMAGE_MACHINE;
flags = 0;
SET_FLAG(flags, IMPORT_FORCE, force);
SET_FLAG(flags, IMPORT_READ_ONLY, read_only);
}
r = fd_verify_safe_flags(fd);
if (r < 0)
return r;
@ -727,15 +810,17 @@ static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
if (!S_ISREG(st.st_mode) && !S_ISFIFO(st.st_mode))
return -EINVAL;
if (!hostname_is_valid(local, 0))
if (!image_name_is_valid(local))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Local name %s is invalid", local);
"Local image name %s is invalid", local);
r = setup_machine_directory(error, m->use_btrfs_subvol, m->use_btrfs_quota);
if (r < 0)
return r;
if (class == IMAGE_MACHINE) {
r = setup_machine_directory(error, m->use_btrfs_subvol, m->use_btrfs_quota);
if (r < 0)
return r;
}
type = streq_ptr(sd_bus_message_get_member(msg), "ImportTar") ?
type = startswith(sd_bus_message_get_member(msg), "ImportTar") ?
TRANSFER_IMPORT_TAR : TRANSFER_IMPORT_RAW;
r = transfer_new(m, &t);
@ -743,8 +828,8 @@ static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
return r;
t->type = type;
t->force_local = force;
t->read_only = read_only;
t->class = class;
t->flags = flags;
t->local = strdup(local);
if (!t->local)
@ -758,19 +843,21 @@ static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
if (r < 0)
return r;
object = t->object_path;
id = t->id;
t = NULL;
r = sd_bus_reply_method_return(msg, "uo", t->id, t->object_path);
if (r < 0)
return r;
return sd_bus_reply_method_return(msg, "uo", id, object);
TAKE_PTR(t);
return 1;
}
static int method_import_fs(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
_cleanup_(transfer_unrefp) Transfer *t = NULL;
int fd, force, read_only, r;
const char *local, *object;
ImageClass class = _IMAGE_CLASS_INVALID;
Manager *m = ASSERT_PTR(userdata);
uint32_t id;
const char *local;
uint64_t flags;
int fd, r;
assert(msg);
@ -785,7 +872,36 @@ static int method_import_fs(sd_bus_message *msg, void *userdata, sd_bus_error *e
if (r == 0)
return 1; /* Will call us back */
r = sd_bus_message_read(msg, "hsbb", &fd, &local, &force, &read_only);
if (endswith(sd_bus_message_get_member(msg), "Ex")) {
const char *sclass;
r = sd_bus_message_read(msg, "hsst", &fd, &local, &sclass, &flags);
if (r < 0)
return r;
class = image_class_from_string(sclass);
if (class < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Image class '%s' not known", sclass);
if (flags & ~(IMPORT_READ_ONLY|IMPORT_FORCE))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Flags 0x%" PRIx64 " invalid", flags);
} else {
int force, read_only;
r = sd_bus_message_read(msg, "hsbb", &fd, &local, &force, &read_only);
if (r < 0)
return r;
class = IMAGE_MACHINE;
flags = 0;
SET_FLAG(flags, IMPORT_FORCE, force);
SET_FLAG(flags, IMPORT_READ_ONLY, read_only);
}
r = fd_verify_safe_flags(fd);
if (r < 0)
return r;
@ -793,21 +909,23 @@ static int method_import_fs(sd_bus_message *msg, void *userdata, sd_bus_error *e
if (r < 0)
return r;
if (!hostname_is_valid(local, 0))
if (!image_name_is_valid(local))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Local name %s is invalid", local);
"Local image name %s is invalid", local);
r = setup_machine_directory(error, m->use_btrfs_subvol, m->use_btrfs_quota);
if (r < 0)
return r;
if (class == IMAGE_MACHINE) {
r = setup_machine_directory(error, m->use_btrfs_subvol, m->use_btrfs_quota);
if (r < 0)
return r;
}
r = transfer_new(m, &t);
if (r < 0)
return r;
t->type = TRANSFER_IMPORT_FS;
t->force_local = force;
t->read_only = read_only;
t->class = class;
t->flags = flags;
t->local = strdup(local);
if (!t->local)
@ -821,21 +939,23 @@ static int method_import_fs(sd_bus_message *msg, void *userdata, sd_bus_error *e
if (r < 0)
return r;
object = t->object_path;
id = t->id;
t = NULL;
r = sd_bus_reply_method_return(msg, "uo", t->id, t->object_path);
if (r < 0)
return r;
return sd_bus_reply_method_return(msg, "uo", id, object);
TAKE_PTR(t);
return 1;
}
static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
_cleanup_(transfer_unrefp) Transfer *t = NULL;
int fd, r;
const char *local, *object, *format;
ImageClass class = _IMAGE_CLASS_INVALID;
Manager *m = ASSERT_PTR(userdata);
const char *local, *format;
TransferType type;
uint64_t flags;
struct stat st;
uint32_t id;
int fd, r;
assert(msg);
@ -850,21 +970,45 @@ static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
if (r == 0)
return 1; /* Will call us back */
r = sd_bus_message_read(msg, "shs", &local, &fd, &format);
if (endswith(sd_bus_message_get_member(msg), "Ex")) {
const char *sclass;
r = sd_bus_message_read(msg, "sshst", &local, &sclass, &fd, &format, &flags);
if (r < 0)
return r;
class = image_class_from_string(sclass);
if (class < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Image class '%s' not known", sclass);
if (flags != 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Flags 0x%" PRIx64 " invalid", flags);
} else {
r = sd_bus_message_read(msg, "shs", &local, &fd, &format);
if (r < 0)
return r;
class = IMAGE_MACHINE;
flags = 0;
}
if (!image_name_is_valid(local))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Local image name %s is invalid", local);
r = fd_verify_safe_flags(fd);
if (r < 0)
return r;
if (!hostname_is_valid(local, 0))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Local name %s is invalid", local);
if (fstat(fd, &st) < 0)
return -errno;
if (!S_ISREG(st.st_mode) && !S_ISFIFO(st.st_mode))
return -EINVAL;
type = streq_ptr(sd_bus_message_get_member(msg), "ExportTar") ?
type = startswith(sd_bus_message_get_member(msg), "ExportTar") ?
TRANSFER_EXPORT_TAR : TRANSFER_EXPORT_RAW;
r = transfer_new(m, &t);
@ -872,6 +1016,8 @@ static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
return r;
t->type = type;
t->class = class;
t->flags = flags;
if (!isempty(format)) {
t->format = strdup(format);
@ -891,21 +1037,23 @@ static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
if (r < 0)
return r;
object = t->object_path;
id = t->id;
t = NULL;
r = sd_bus_reply_method_return(msg, "uo", t->id, t->object_path);
if (r < 0)
return r;
return sd_bus_reply_method_return(msg, "uo", id, object);
TAKE_PTR(t);
return 1;
}
static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
_cleanup_(transfer_unrefp) Transfer *t = NULL;
const char *remote, *local, *verify, *object;
ImageClass class = _IMAGE_CLASS_INVALID;
const char *remote, *local, *verify;
Manager *m = ASSERT_PTR(userdata);
ImportVerify v;
TransferType type;
int force, r;
uint32_t id;
uint64_t flags;
ImportVerify v;
int r;
assert(msg);
@ -920,9 +1068,33 @@ static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_er
if (r == 0)
return 1; /* Will call us back */
r = sd_bus_message_read(msg, "sssb", &remote, &local, &verify, &force);
if (r < 0)
return r;
if (endswith(sd_bus_message_get_member(msg), "Ex")) {
const char *sclass;
r = sd_bus_message_read(msg, "sssst", &remote, &local, &sclass, &verify, &flags);
if (r < 0)
return r;
class = image_class_from_string(sclass);
if (class < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Image class '%s' not known", sclass);
if (flags & ~(IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_PULL_KEEP_DOWNLOAD))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Flags 0x%" PRIx64 " invalid", flags);
} else {
int force;
r = sd_bus_message_read(msg, "sssb", &remote, &local, &verify, &force);
if (r < 0)
return r;
class = IMAGE_MACHINE;
flags = 0;
SET_FLAG(flags, IMPORT_FORCE, force);
}
if (!http_url_is_valid(remote) && !file_url_is_valid(remote))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
@ -930,9 +1102,9 @@ static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_er
if (isempty(local))
local = NULL;
else if (!hostname_is_valid(local, 0))
else if (!image_name_is_valid(local))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Local name %s is invalid", local);
"Local image name %s is invalid", local);
if (isempty(verify))
v = IMPORT_VERIFY_SIGNATURE;
@ -942,11 +1114,13 @@ static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_er
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Unknown verification mode %s", verify);
r = setup_machine_directory(error, m->use_btrfs_subvol, m->use_btrfs_quota);
if (r < 0)
return r;
if (class == IMAGE_MACHINE) {
r = setup_machine_directory(error, m->use_btrfs_subvol, m->use_btrfs_quota);
if (r < 0)
return r;
}
type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ?
type = startswith(sd_bus_message_get_member(msg), "PullTar") ?
TRANSFER_PULL_TAR : TRANSFER_PULL_RAW;
if (manager_find(m, type, remote))
@ -959,7 +1133,8 @@ static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_er
t->type = type;
t->verify = v;
t->force_local = force;
t->flags = flags;
t->class = class;
t->remote = strdup(remote);
if (!t->remote)
@ -975,40 +1150,81 @@ static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_er
if (r < 0)
return r;
object = t->object_path;
id = t->id;
t = NULL;
r = sd_bus_reply_method_return(msg, "uo", t->id, t->object_path);
if (r < 0)
return r;
return sd_bus_reply_method_return(msg, "uo", id, object);
TAKE_PTR(t);
return 1;
}
static int method_list_transfers(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Manager *m = ASSERT_PTR(userdata);
ImageClass class = _IMAGE_CLASS_INVALID;
Transfer *t;
int r;
assert(msg);
bool ex = endswith(sd_bus_message_get_member(msg), "Ex");
if (ex) {
const char *sclass;
uint64_t flags;
r = sd_bus_message_read(msg, "st", &sclass, &flags);
if (r < 0)
return bus_log_parse_error(r);
if (!isempty(sclass)) {
class = image_class_from_string(sclass);
if (class < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Image class '%s' not known", sclass);
}
if (flags != 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Flags 0x%" PRIx64 " invalid", flags);
}
r = sd_bus_message_new_method_return(msg, &reply);
if (r < 0)
return r;
r = sd_bus_message_open_container(reply, 'a', "(usssdo)");
if (ex)
r = sd_bus_message_open_container(reply, 'a', "(ussssdo)");
else
r = sd_bus_message_open_container(reply, 'a', "(usssdo)");
if (r < 0)
return r;
HASHMAP_FOREACH(t, m->transfers) {
r = sd_bus_message_append(
reply,
"(usssdo)",
t->id,
transfer_type_to_string(t->type),
t->remote,
t->local,
transfer_percent_as_double(t),
t->object_path);
if (class >= 0 && class != t->class)
continue;
if (ex)
r = sd_bus_message_append(
reply,
"(ussssdo)",
t->id,
transfer_type_to_string(t->type),
t->remote,
t->local,
image_class_to_string(t->class),
transfer_percent_as_double(t),
t->object_path);
else
r = sd_bus_message_append(
reply,
"(usssdo)",
t->id,
transfer_type_to_string(t->type),
t->remote,
t->local,
transfer_percent_as_double(t),
t->object_path);
if (r < 0)
return r;
}
@ -1054,7 +1270,7 @@ static int method_cancel_transfer(sd_bus_message *msg, void *userdata, sd_bus_er
r = bus_verify_polkit_async(
msg,
"org.freedesktop.import1.pull",
"org.freedesktop.import1.cancel",
/* details= */ NULL,
&m->polkit_registry,
error);
@ -1080,6 +1296,86 @@ static int method_cancel_transfer(sd_bus_message *msg, void *userdata, sd_bus_er
return sd_bus_reply_method_return(msg, NULL);
}
static int method_list_images(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
ImageClass class = _IMAGE_CLASS_INVALID;
int r;
assert(msg);
const char *sclass;
uint64_t flags;
r = sd_bus_message_read(msg, "st", &sclass, &flags);
if (r < 0)
return r;
if (!isempty(sclass)) {
class = image_class_from_string(sclass);
if (class < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Image class '%s' not known", sclass);
}
if (flags != 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Flags 0x%" PRIx64 " invalid", flags);
r = sd_bus_message_new_method_return(msg, &reply);
if (r < 0)
return r;
r = sd_bus_message_open_container(reply, 'a', "(ssssbtttttt)");
if (r < 0)
return r;
for (ImageClass c = class < 0 ? 0 : class;
class < 0 ? (c < _IMAGE_CLASS_MAX) : (c == class);
c++) {
_cleanup_(hashmap_freep) Hashmap *h = NULL;
h = hashmap_new(&image_hash_ops);
if (!h)
return -ENOMEM;
r = image_discover(c, /* root= */ NULL, h);
if (r < 0) {
if (class >= 0)
return r;
log_warning_errno(r, "Failed to discover images of type %s: %m", image_class_to_string(c));
continue;
}
Image *i;
HASHMAP_FOREACH(i, h) {
r = sd_bus_message_append(
reply,
"(ssssbtttttt)",
image_class_to_string(i->class),
i->name,
image_type_to_string(i->type),
i->path,
i->read_only,
i->crtime,
i->mtime,
i->usage,
i->usage_exclusive,
i->limit,
i->limit_exclusive);
if (r < 0)
return r;
}
}
r = sd_bus_message_close_container(reply);
if (r < 0)
return r;
return sd_bus_send(NULL, reply, NULL);
}
static int property_get_progress(
sd_bus *bus,
const char *path,
@ -1182,6 +1478,10 @@ static const sd_bus_vtable transfer_vtable[] = {
SD_BUS_PARAM(priority)
SD_BUS_PARAM(line),
0),
SD_BUS_SIGNAL_WITH_NAMES("ProgressUpdate",
"d",
SD_BUS_PARAM(progress),
0),
SD_BUS_VTABLE_END,
};
@ -1207,6 +1507,17 @@ static const sd_bus_vtable manager_vtable[] = {
SD_BUS_PARAM(transfer_path),
method_import_tar_or_raw,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("ImportTarEx",
"hsst",
SD_BUS_PARAM(fd)
SD_BUS_PARAM(local_name)
SD_BUS_PARAM(class)
SD_BUS_PARAM(flags),
"uo",
SD_BUS_PARAM(transfer_id)
SD_BUS_PARAM(transfer_path),
method_import_tar_or_raw,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("ImportRaw",
"hsbb",
SD_BUS_PARAM(fd)
@ -1218,6 +1529,17 @@ static const sd_bus_vtable manager_vtable[] = {
SD_BUS_PARAM(transfer_path),
method_import_tar_or_raw,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("ImportRawEx",
"hsst",
SD_BUS_PARAM(fd)
SD_BUS_PARAM(local_name)
SD_BUS_PARAM(class)
SD_BUS_PARAM(flags),
"uo",
SD_BUS_PARAM(transfer_id)
SD_BUS_PARAM(transfer_path),
method_import_tar_or_raw,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("ImportFileSystem",
"hsbb",
SD_BUS_PARAM(fd)
@ -1229,6 +1551,17 @@ static const sd_bus_vtable manager_vtable[] = {
SD_BUS_PARAM(transfer_path),
method_import_fs,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("ImportFileSystemEx",
"hsst",
SD_BUS_PARAM(fd)
SD_BUS_PARAM(local_name)
SD_BUS_PARAM(class)
SD_BUS_PARAM(flags),
"uo",
SD_BUS_PARAM(transfer_id)
SD_BUS_PARAM(transfer_path),
method_import_fs,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("ExportTar",
"shs",
SD_BUS_PARAM(local_name)
@ -1239,6 +1572,18 @@ static const sd_bus_vtable manager_vtable[] = {
SD_BUS_PARAM(transfer_path),
method_export_tar_or_raw,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("ExportTarEx",
"sshst",
SD_BUS_PARAM(local_name)
SD_BUS_PARAM(class)
SD_BUS_PARAM(fd)
SD_BUS_PARAM(format)
SD_BUS_PARAM(flags),
"uo",
SD_BUS_PARAM(transfer_id)
SD_BUS_PARAM(transfer_path),
method_export_tar_or_raw,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("ExportRaw",
"shs",
SD_BUS_PARAM(local_name)
@ -1249,6 +1594,18 @@ static const sd_bus_vtable manager_vtable[] = {
SD_BUS_PARAM(transfer_path),
method_export_tar_or_raw,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("ExportRawEx",
"sshst",
SD_BUS_PARAM(local_name)
SD_BUS_PARAM(class)
SD_BUS_PARAM(fd)
SD_BUS_PARAM(format)
SD_BUS_PARAM(flags),
"uo",
SD_BUS_PARAM(transfer_id)
SD_BUS_PARAM(transfer_path),
method_export_tar_or_raw,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("PullTar",
"sssb",
SD_BUS_PARAM(url)
@ -1260,6 +1617,18 @@ static const sd_bus_vtable manager_vtable[] = {
SD_BUS_PARAM(transfer_path),
method_pull_tar_or_raw,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("PullTarEx",
"sssst",
SD_BUS_PARAM(url)
SD_BUS_PARAM(local_name)
SD_BUS_PARAM(class)
SD_BUS_PARAM(verify_mode)
SD_BUS_PARAM(flags),
"uo",
SD_BUS_PARAM(transfer_id)
SD_BUS_PARAM(transfer_path),
method_pull_tar_or_raw,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("PullRaw",
"sssb",
SD_BUS_PARAM(url)
@ -1271,18 +1640,46 @@ static const sd_bus_vtable manager_vtable[] = {
SD_BUS_PARAM(transfer_path),
method_pull_tar_or_raw,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("PullRawEx",
"sssst",
SD_BUS_PARAM(url)
SD_BUS_PARAM(local_name)
SD_BUS_PARAM(class)
SD_BUS_PARAM(verify_mode)
SD_BUS_PARAM(flags),
"uo",
SD_BUS_PARAM(transfer_id)
SD_BUS_PARAM(transfer_path),
method_pull_tar_or_raw,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("ListTransfers",
NULL,,
"a(usssdo)",
SD_BUS_PARAM(transfers),
method_list_transfers,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("ListTransfersEx",
"st",
SD_BUS_PARAM(class)
SD_BUS_PARAM(flags),
"a(ussssdo)",
SD_BUS_PARAM(transfers),
method_list_transfers,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("CancelTransfer",
"u",
SD_BUS_PARAM(transfer_id),
NULL,,
method_cancel_transfer,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("ListImages",
"st",
SD_BUS_PARAM(class)
SD_BUS_PARAM(flags),
"a(ssssbtttttt)",
SD_BUS_PARAM(images),
method_list_images,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_SIGNAL_WITH_NAMES("TransferNew",
"uo",
@ -1374,7 +1771,7 @@ static int run(int argc, char *argv[]) {
umask(0022);
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, SIGRTMIN+18) >= 0);
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD) >= 0);
r = manager_new(&m);
if (r < 0)

View file

@ -100,6 +100,12 @@ executables += [
'link_with' : common_libs,
'dependencies' : common_deps,
},
executable_template + {
'name' : 'importctl',
'public' : true,
'conditions' : ['ENABLE_IMPORTD'],
'sources' : files('importctl.c'),
},
test_template + {
'sources' : files(
'test-qcow2.c',

View file

@ -42,6 +42,10 @@
send_interface="org.freedesktop.import1.Manager"
send_member="ListTransfers"/>
<allow send_destination="org.freedesktop.import1"
send_interface="org.freedesktop.import1.Manager"
send_member="ListTransfersEx"/>
<allow send_destination="org.freedesktop.import1"
send_interface="org.freedesktop.import1.Manager"
send_member="CancelTransfer"/>
@ -50,34 +54,66 @@
send_interface="org.freedesktop.import1.Manager"
send_member="ImportTar"/>
<allow send_destination="org.freedesktop.import1"
send_interface="org.freedesktop.import1.Manager"
send_member="ImportTarEx"/>
<allow send_destination="org.freedesktop.import1"
send_interface="org.freedesktop.import1.Manager"
send_member="ImportRaw"/>
<allow send_destination="org.freedesktop.import1"
send_interface="org.freedesktop.import1.Manager"
send_member="ImportRawEx"/>
<allow send_destination="org.freedesktop.import1"
send_interface="org.freedesktop.import1.Manager"
send_member="ImportFileSystem"/>
<allow send_destination="org.freedesktop.import1"
send_interface="org.freedesktop.import1.Manager"
send_member="ImportFileSystemEx"/>
<allow send_destination="org.freedesktop.import1"
send_interface="org.freedesktop.import1.Manager"
send_member="ExportTar"/>
<allow send_destination="org.freedesktop.import1"
send_interface="org.freedesktop.import1.Manager"
send_member="ExportTarEx"/>
<allow send_destination="org.freedesktop.import1"
send_interface="org.freedesktop.import1.Manager"
send_member="ExportRaw"/>
<allow send_destination="org.freedesktop.import1"
send_interface="org.freedesktop.import1.Manager"
send_member="ExportRawEx"/>
<allow send_destination="org.freedesktop.import1"
send_interface="org.freedesktop.import1.Manager"
send_member="PullTar"/>
<allow send_destination="org.freedesktop.import1"
send_interface="org.freedesktop.import1.Manager"
send_member="PullTarEx"/>
<allow send_destination="org.freedesktop.import1"
send_interface="org.freedesktop.import1.Manager"
send_member="PullRaw"/>
<allow send_destination="org.freedesktop.import1"
send_interface="org.freedesktop.import1.Manager"
send_member="PullRawEx"/>
<allow send_destination="org.freedesktop.import1"
send_interface="org.freedesktop.import1.Transfer"
send_member="Cancel"/>
<allow send_destination="org.freedesktop.import1"
send_interface="org.freedesktop.import1.Transfer"
send_member="ListImages"/>
<allow receive_sender="org.freedesktop.import1"/>
</policy>

View file

@ -19,8 +19,8 @@
<vendor_url>https://systemd.io</vendor_url>
<action id="org.freedesktop.import1.import">
<description gettext-domain="systemd">Import a VM or container image</description>
<message gettext-domain="systemd">Authentication is required to import a VM or container image</message>
<description gettext-domain="systemd">Import a disk image</description>
<message gettext-domain="systemd">Authentication is required to import an image</message>
<defaults>
<allow_any>auth_admin</allow_any>
<allow_inactive>auth_admin</allow_inactive>
@ -29,8 +29,8 @@
</action>
<action id="org.freedesktop.import1.export">
<description gettext-domain="systemd">Export a VM or container image</description>
<message gettext-domain="systemd">Authentication is required to export a VM or container image</message>
<description gettext-domain="systemd">Export a disk image</description>
<message gettext-domain="systemd">Authentication is required to export disk image</message>
<defaults>
<allow_any>auth_admin</allow_any>
<allow_inactive>auth_admin</allow_inactive>
@ -39,8 +39,18 @@
</action>
<action id="org.freedesktop.import1.pull">
<description gettext-domain="systemd">Download a VM or container image</description>
<message gettext-domain="systemd">Authentication is required to download a VM or container image</message>
<description gettext-domain="systemd">Download a disk image</description>
<message gettext-domain="systemd">Authentication is required to download a disk image</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="org.freedesktop.import1.cancel">
<description gettext-domain="systemd">Cancel transfer of a disk image</description>
<message gettext-domain="systemd">Authentication is required to cancel the ongoing transfer of a disk image</message>
<defaults>
<allow_any>auth_admin</allow_any>
<allow_inactive>auth_admin</allow_inactive>

View file

@ -7,6 +7,7 @@
#include "capability-util.h"
#include "copy.h"
#include "dirent-util.h"
#include "discover-image.h"
#include "escape.h"
#include "fd-util.h"
#include "hostname-util.h"
@ -37,11 +38,9 @@ int pull_find_old_etags(
int r;
assert(url);
assert(image_root);
assert(etags);
if (!image_root)
image_root = "/var/lib/machines";
_cleanup_free_ char *escaped_url = xescape(url, FILENAME_ESCAPE);
if (!escaped_url)
return -ENOMEM;
@ -128,11 +127,9 @@ int pull_make_path(const char *url, const char *etag, const char *image_root, co
char *path;
assert(url);
assert(image_root);
assert(ret);
if (!image_root)
image_root = "/var/lib/machines";
escaped_url = xescape(url, FILENAME_ESCAPE);
if (!escaped_url)
return -ENOMEM;
@ -643,12 +640,12 @@ int pull_job_restart_with_sha256sum(PullJob *j, char **ret) {
return 1;
}
bool pull_validate_local(const char *name, PullFlags flags) {
bool pull_validate_local(const char *name, ImportFlags flags) {
if (FLAGS_SET(flags, PULL_DIRECT))
if (FLAGS_SET(flags, IMPORT_DIRECT))
return path_is_valid(name);
return hostname_is_valid(name, 0);
return image_name_is_valid(name);
}
int pull_url_needs_checksum(const char *url) {

View file

@ -3,27 +3,10 @@
#include <stdbool.h>
#include "import-common.h"
#include "import-util.h"
#include "pull-job.h"
typedef enum PullFlags {
PULL_FORCE = 1 << 0, /* replace existing image */
PULL_READ_ONLY = 1 << 1, /* make generated image read-only */
PULL_SETTINGS = 1 << 2, /* download .nspawn settings file */
PULL_ROOTHASH = 1 << 3, /* only for raw: download .roothash file for verity */
PULL_ROOTHASH_SIGNATURE = 1 << 4, /* only for raw: download .roothash.p7s file for verity */
PULL_VERITY = 1 << 5, /* only for raw: download .verity file for verity */
PULL_BTRFS_SUBVOL = 1 << 6, /* tar: preferably create images as btrfs subvols */
PULL_BTRFS_QUOTA = 1 << 7, /* tar: set up btrfs quota for new subvolume as child of parent subvolume */
PULL_CONVERT_QCOW2 = 1 << 8, /* raw: if we detect a qcow2 image, unpack it */
PULL_DIRECT = 1 << 9, /* download without rename games */
PULL_SYNC = 1 << 10, /* fsync() right before we are done */
/* The supported flags for the tar and the raw pulling */
PULL_FLAGS_MASK_TAR = PULL_FORCE|PULL_READ_ONLY|PULL_SETTINGS|PULL_BTRFS_SUBVOL|PULL_BTRFS_QUOTA|PULL_DIRECT|PULL_SYNC,
PULL_FLAGS_MASK_RAW = PULL_FORCE|PULL_READ_ONLY|PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY|PULL_CONVERT_QCOW2|PULL_DIRECT|PULL_SYNC,
} PullFlags;
int pull_find_old_etags(const char *url, const char *root, int dt, const char *prefix, const char *suffix, char ***etags);
int pull_make_path(const char *url, const char *etag, const char *image_root, const char *prefix, const char *suffix, char **ret);
@ -44,6 +27,6 @@ int verification_style_from_url(const char *url, VerificationStyle *style);
int pull_job_restart_with_sha256sum(PullJob *job, char **ret);
bool pull_validate_local(const char *name, PullFlags flags);
bool pull_validate_local(const char *name, ImportFlags flags);
int pull_url_needs_checksum(const char *url);

View file

@ -187,7 +187,7 @@ void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
}
}
r = log_error_errno(
r = log_notice_errno(
status == 404 ? SYNTHETIC_ERRNO(ENOMEDIUM) : SYNTHETIC_ERRNO(EIO), /* Make the most common error recognizable */
"HTTP request to %s failed with code %li.", j->url, status);
goto finish;

View file

@ -42,7 +42,7 @@ struct RawPull {
sd_event *event;
CurlGlue *glue;
PullFlags flags;
ImportFlags flags;
ImportVerify verify;
char *image_root;
@ -60,7 +60,7 @@ struct RawPull {
void *userdata;
char *local; /* In PULL_DIRECT mode the path we are supposed to place things in, otherwise the
* machine name of the final copy we make */
* image name of the final copy we make */
char *final_path;
char *temp_path;
@ -127,8 +127,9 @@ int raw_pull_new(
int r;
assert(ret);
assert(image_root);
root = strdup(image_root ?: "/var/lib/machines");
root = strdup(image_root);
if (!root)
return -ENOMEM;
@ -244,9 +245,9 @@ static int raw_pull_maybe_convert_qcow2(RawPull *i) {
assert(i);
assert(i->raw_job);
assert(!FLAGS_SET(i->flags, PULL_DIRECT));
assert(!FLAGS_SET(i->flags, IMPORT_DIRECT));
if (!FLAGS_SET(i->flags, PULL_CONVERT_QCOW2))
if (!FLAGS_SET(i->flags, IMPORT_CONVERT_QCOW2))
return 0;
assert(i->final_path);
@ -310,7 +311,7 @@ static int raw_pull_copy_auxiliary_file(
const char *suffix,
char **path /* input + output (!) */) {
const char *local;
_cleanup_free_ char *local = NULL;
int r;
assert(i);
@ -321,21 +322,29 @@ static int raw_pull_copy_auxiliary_file(
if (r < 0)
return r;
local = strjoina(i->image_root, "/", i->local, suffix);
local = strjoin(i->image_root, "/", i->local, suffix);
if (!local)
return log_oom();
r = copy_file_atomic(
*path,
local,
0644,
COPY_REFLINK |
(FLAGS_SET(i->flags, PULL_FORCE) ? COPY_REPLACE : 0) |
(FLAGS_SET(i->flags, PULL_SYNC) ? COPY_FSYNC_FULL : 0));
if (FLAGS_SET(i->flags, IMPORT_PULL_KEEP_DOWNLOAD))
r = copy_file_atomic(
*path,
local,
0644,
COPY_REFLINK |
(FLAGS_SET(i->flags, IMPORT_FORCE) ? COPY_REPLACE : 0) |
(FLAGS_SET(i->flags, IMPORT_SYNC) ? COPY_FSYNC_FULL : 0));
else
r = install_file(AT_FDCWD, *path,
AT_FDCWD, local,
(i->flags & IMPORT_FORCE ? INSTALL_REPLACE : 0) |
(i->flags & IMPORT_SYNC ? INSTALL_SYNCFS : 0));
if (r == -EEXIST)
log_warning_errno(r, "File %s already exists, not replacing.", local);
else if (r == -ENOENT)
log_debug_errno(r, "Skipping creation of auxiliary file, since none was found.");
else if (r < 0)
log_warning_errno(r, "Failed to copy file %s, ignoring: %m", local);
log_warning_errno(r, "Failed to install file %s, ignoring: %m", local);
else
log_info("Created new file %s.", local);
@ -344,14 +353,12 @@ static int raw_pull_copy_auxiliary_file(
static int raw_pull_make_local_copy(RawPull *i) {
_cleanup_(unlink_and_freep) char *tp = NULL;
_cleanup_free_ char *f = NULL;
_cleanup_close_ int dfd = -EBADF;
const char *p;
_cleanup_free_ char *p = NULL;
int r;
assert(i);
assert(i->raw_job);
assert(!FLAGS_SET(i->flags, PULL_DIRECT));
assert(!FLAGS_SET(i->flags, IMPORT_DIRECT));
if (!i->local)
return 0;
@ -374,62 +381,73 @@ static int raw_pull_make_local_copy(RawPull *i) {
return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
}
p = strjoina(i->image_root, "/", i->local, ".raw");
r = tempfn_random(p, NULL, &f);
if (r < 0)
p = strjoin(i->image_root, "/", i->local, ".raw");
if (!p)
return log_oom();
dfd = open(f, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
if (dfd < 0)
return log_error_errno(errno, "Failed to create writable copy of image: %m");
const char *source;
if (FLAGS_SET(i->flags, IMPORT_PULL_KEEP_DOWNLOAD)) {
_cleanup_close_ int dfd = -EBADF;
_cleanup_free_ char *f = NULL;
tp = TAKE_PTR(f);
r = tempfn_random(p, NULL, &f);
if (r < 0)
return log_oom();
/* Turn off COW writing. This should greatly improve performance on COW file systems like btrfs,
* since it reduces fragmentation caused by not allowing in-place writes. */
(void) import_set_nocow_and_log(dfd, tp);
dfd = open(f, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
if (dfd < 0)
return log_error_errno(errno, "Failed to create writable copy of image: %m");
r = copy_bytes(i->raw_job->disk_fd, dfd, UINT64_MAX, COPY_REFLINK);
if (r < 0)
return log_error_errno(r, "Failed to make writable copy of image: %m");
tp = TAKE_PTR(f);
(void) copy_times(i->raw_job->disk_fd, dfd, COPY_CRTIME);
(void) copy_xattr(i->raw_job->disk_fd, NULL, dfd, NULL, 0);
/* Turn off COW writing. This should greatly improve performance on COW file systems like btrfs,
* since it reduces fragmentation caused by not allowing in-place writes. */
(void) import_set_nocow_and_log(dfd, tp);
dfd = safe_close(dfd);
r = copy_bytes(i->raw_job->disk_fd, dfd, UINT64_MAX, COPY_REFLINK);
if (r < 0)
return log_error_errno(r, "Failed to make writable copy of image: %m");
r = install_file(AT_FDCWD, tp,
(void) copy_times(i->raw_job->disk_fd, dfd, COPY_CRTIME);
(void) copy_xattr(i->raw_job->disk_fd, NULL, dfd, NULL, 0);
dfd = safe_close(dfd);
source = tp;
} else
source = i->final_path;
r = install_file(AT_FDCWD, source,
AT_FDCWD, p,
(i->flags & PULL_FORCE ? INSTALL_REPLACE : 0) |
(i->flags & PULL_READ_ONLY ? INSTALL_READ_ONLY : 0) |
(i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
(i->flags & IMPORT_FORCE ? INSTALL_REPLACE : 0) |
(i->flags & IMPORT_READ_ONLY ? INSTALL_READ_ONLY : 0) |
(i->flags & IMPORT_SYNC ? INSTALL_FSYNC_FULL : 0));
if (r < 0)
return log_error_errno(errno, "Failed to move local image into place '%s': %m", p);
return log_error_errno(r, "Failed to move local image into place '%s': %m", p);
tp = mfree(tp);
log_info("Created new local image '%s'.", i->local);
if (FLAGS_SET(i->flags, PULL_SETTINGS)) {
if (FLAGS_SET(i->flags, IMPORT_PULL_SETTINGS)) {
r = raw_pull_copy_auxiliary_file(i, ".nspawn", &i->settings_path);
if (r < 0)
return r;
}
if (FLAGS_SET(i->flags, PULL_ROOTHASH)) {
if (FLAGS_SET(i->flags, IMPORT_PULL_ROOTHASH)) {
r = raw_pull_copy_auxiliary_file(i, ".roothash", &i->roothash_path);
if (r < 0)
return r;
}
if (FLAGS_SET(i->flags, PULL_ROOTHASH_SIGNATURE)) {
if (FLAGS_SET(i->flags, IMPORT_PULL_ROOTHASH_SIGNATURE)) {
r = raw_pull_copy_auxiliary_file(i, ".roothash.p7s", &i->roothash_signature_path);
if (r < 0)
return r;
}
if (FLAGS_SET(i->flags, PULL_VERITY)) {
if (FLAGS_SET(i->flags, IMPORT_PULL_VERITY)) {
r = raw_pull_copy_auxiliary_file(i, ".verity", &i->verity_path);
if (r < 0)
return r;
@ -485,7 +503,7 @@ static int raw_pull_rename_auxiliary_file(
AT_FDCWD, *temp_path,
AT_FDCWD, *path,
INSTALL_READ_ONLY|
(i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
(i->flags & IMPORT_SYNC ? INSTALL_FSYNC_FULL : 0));
if (r < 0)
return log_error_errno(r, "Failed to move '%s' into place: %m", *path);
@ -588,7 +606,7 @@ static void raw_pull_job_on_finished(PullJob *j) {
goto finish;
}
if (i->flags & PULL_DIRECT) {
if (i->flags & IMPORT_DIRECT) {
assert(!i->settings_job);
assert(!i->roothash_job);
assert(!i->roothash_signature_job);
@ -599,8 +617,8 @@ static void raw_pull_job_on_finished(PullJob *j) {
if (i->local) {
r = install_file(AT_FDCWD, i->local,
AT_FDCWD, NULL,
((i->flags & PULL_READ_ONLY) && i->offset == UINT64_MAX ? INSTALL_READ_ONLY : 0) |
(i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
((i->flags & IMPORT_READ_ONLY) && i->offset == UINT64_MAX ? INSTALL_READ_ONLY : 0) |
(i->flags & IMPORT_SYNC ? INSTALL_FSYNC_FULL : 0));
if (r < 0) {
log_error_errno(r, "Failed to finalize raw file to '%s': %m", i->local);
goto finish;
@ -627,8 +645,8 @@ static void raw_pull_job_on_finished(PullJob *j) {
r = install_file(AT_FDCWD, i->temp_path,
AT_FDCWD, i->final_path,
INSTALL_READ_ONLY|
(i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
(i->flags & IMPORT_PULL_KEEP_DOWNLOAD ? INSTALL_READ_ONLY : 0) |
(i->flags & IMPORT_SYNC ? INSTALL_FSYNC_FULL : 0));
if (r < 0) {
log_error_errno(r, "Failed to move raw file to '%s': %m", i->final_path);
goto finish;
@ -694,7 +712,7 @@ static int raw_pull_job_on_open_disk_generic(
assert(extra);
assert(temp_path);
assert(!FLAGS_SET(i->flags, PULL_DIRECT));
assert(!FLAGS_SET(i->flags, IMPORT_DIRECT));
if (!*temp_path) {
r = tempfn_random_child(i->image_root, extra, temp_path);
@ -722,7 +740,7 @@ static int raw_pull_job_on_open_disk_raw(PullJob *j) {
assert(i->raw_job == j);
assert(j->disk_fd < 0);
if (i->flags & PULL_DIRECT) {
if (i->flags & IMPORT_DIRECT) {
if (!i->local) { /* If no local name specified, the pull job will write its data to stdout */
j->disk_fd = STDOUT_FILENO;
@ -816,7 +834,7 @@ int raw_pull_start(
const char *local,
uint64_t offset,
uint64_t size_max,
PullFlags flags,
ImportFlags flags,
ImportVerify verify,
const char *checksum) {
@ -827,10 +845,10 @@ int raw_pull_start(
assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX);
assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0);
assert((verify < 0) || !checksum);
assert(!(flags & ~PULL_FLAGS_MASK_RAW));
assert(offset == UINT64_MAX || FLAGS_SET(flags, PULL_DIRECT));
assert(!(flags & (PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY)) || !(flags & PULL_DIRECT));
assert(!(flags & (PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY)) || !checksum);
assert(!(flags & ~IMPORT_PULL_FLAGS_MASK_RAW));
assert(offset == UINT64_MAX || FLAGS_SET(flags, IMPORT_DIRECT));
assert(!(flags & (IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY)) || !(flags & IMPORT_DIRECT));
assert(!(flags & (IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY)) || !checksum);
if (!http_url_is_valid(url) && !file_url_is_valid(url))
return -EINVAL;
@ -880,7 +898,7 @@ int raw_pull_start(
if (offset != UINT64_MAX)
i->raw_job->offset = i->offset = offset;
if (!FLAGS_SET(flags, PULL_DIRECT)) {
if (!FLAGS_SET(flags, IMPORT_DIRECT)) {
r = pull_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags);
if (r < 0)
return r;
@ -898,7 +916,7 @@ int raw_pull_start(
if (r < 0)
return r;
if (FLAGS_SET(flags, PULL_SETTINGS)) {
if (FLAGS_SET(flags, IMPORT_PULL_SETTINGS)) {
r = pull_make_auxiliary_job(
&i->settings_job,
url,
@ -913,7 +931,7 @@ int raw_pull_start(
return r;
}
if (FLAGS_SET(flags, PULL_ROOTHASH)) {
if (FLAGS_SET(flags, IMPORT_PULL_ROOTHASH)) {
r = pull_make_auxiliary_job(
&i->roothash_job,
url,
@ -928,7 +946,7 @@ int raw_pull_start(
return r;
}
if (FLAGS_SET(flags, PULL_ROOTHASH_SIGNATURE)) {
if (FLAGS_SET(flags, IMPORT_PULL_ROOTHASH_SIGNATURE)) {
r = pull_make_auxiliary_job(
&i->roothash_signature_job,
url,
@ -943,7 +961,7 @@ int raw_pull_start(
return r;
}
if (FLAGS_SET(flags, PULL_VERITY)) {
if (FLAGS_SET(flags, IMPORT_PULL_VERITY)) {
r = pull_make_auxiliary_job(
&i->verity_job,
url,
@ -972,7 +990,7 @@ int raw_pull_start(
continue;
j->on_progress = raw_pull_job_on_progress;
j->sync = FLAGS_SET(flags, PULL_SYNC);
j->sync = FLAGS_SET(flags, IMPORT_SYNC);
r = pull_job_begin(j);
if (r < 0)

View file

@ -16,4 +16,4 @@ RawPull* raw_pull_unref(RawPull *pull);
DEFINE_TRIVIAL_CLEANUP_FUNC(RawPull*, raw_pull_unref);
int raw_pull_start(RawPull *pull, const char *url, const char *local, uint64_t offset, uint64_t size_max, PullFlags flags, ImportVerify verify, const char *checksum);
int raw_pull_start(RawPull *pull, const char *url, const char *local, uint64_t offset, uint64_t size_max, ImportFlags flags, ImportVerify verify, const char *checksum);

View file

@ -41,7 +41,7 @@ struct TarPull {
sd_event *event;
CurlGlue *glue;
PullFlags flags;
ImportFlags flags;
ImportVerify verify;
char *image_root;
@ -106,9 +106,10 @@ int tar_pull_new(
_cleanup_free_ char *root = NULL;
int r;
assert(image_root);
assert(ret);
root = strdup(image_root ?: "/var/lib/machines");
root = strdup(image_root);
if (!root)
return -ENOMEM;
@ -219,7 +220,8 @@ static int tar_pull_determine_path(
static int tar_pull_make_local_copy(TarPull *i) {
_cleanup_(rm_rf_subvolume_and_freep) char *t = NULL;
const char *p;
_cleanup_free_ char *p = NULL;
const char *source;
int r;
assert(i);
@ -230,30 +232,37 @@ static int tar_pull_make_local_copy(TarPull *i) {
assert(i->final_path);
p = prefix_roota(i->image_root, i->local);
p = path_join(i->image_root, i->local);
if (!p)
return log_oom();
r = tempfn_random(p, NULL, &t);
if (r < 0)
return log_error_errno(r, "Failed to generate temporary filename for %s: %m", p);
if (FLAGS_SET(i->flags, IMPORT_PULL_KEEP_DOWNLOAD)) {
r = tempfn_random(p, NULL, &t);
if (r < 0)
return log_error_errno(r, "Failed to generate temporary filename for %s: %m", p);
if (i->flags & PULL_BTRFS_SUBVOL)
r = btrfs_subvol_snapshot_at(
AT_FDCWD, i->final_path,
AT_FDCWD, t,
(i->flags & PULL_BTRFS_QUOTA ? BTRFS_SNAPSHOT_QUOTA : 0)|
BTRFS_SNAPSHOT_FALLBACK_COPY|
BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|
BTRFS_SNAPSHOT_RECURSIVE);
else
r = copy_tree(i->final_path, t, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_HARDLINKS, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to create local image: %m");
if (i->flags & IMPORT_BTRFS_SUBVOL)
r = btrfs_subvol_snapshot_at(
AT_FDCWD, i->final_path,
AT_FDCWD, t,
(i->flags & IMPORT_BTRFS_QUOTA ? BTRFS_SNAPSHOT_QUOTA : 0)|
BTRFS_SNAPSHOT_FALLBACK_COPY|
BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|
BTRFS_SNAPSHOT_RECURSIVE);
else
r = copy_tree(i->final_path, t, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_HARDLINKS, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to create local image: %m");
r = install_file(AT_FDCWD, t,
source = t;
} else
source = i->final_path;
r = install_file(AT_FDCWD, source,
AT_FDCWD, p,
(i->flags & PULL_FORCE ? INSTALL_REPLACE : 0) |
(i->flags & PULL_READ_ONLY ? INSTALL_READ_ONLY : 0) |
(i->flags & PULL_SYNC ? INSTALL_SYNCFS : 0));
(i->flags & IMPORT_FORCE ? INSTALL_REPLACE : 0) |
(i->flags & IMPORT_READ_ONLY ? INSTALL_READ_ONLY : 0) |
(i->flags & IMPORT_SYNC ? INSTALL_SYNCFS : 0));
if (r < 0)
return log_error_errno(r, "Failed to install local image '%s': %m", p);
@ -261,29 +270,37 @@ static int tar_pull_make_local_copy(TarPull *i) {
log_info("Created new local image '%s'.", i->local);
if (FLAGS_SET(i->flags, PULL_SETTINGS)) {
const char *local_settings;
if (FLAGS_SET(i->flags, IMPORT_PULL_SETTINGS)) {
_cleanup_free_ char *local_settings = NULL;
assert(i->settings_job);
r = tar_pull_determine_path(i, ".nspawn", &i->settings_path);
if (r < 0)
return r;
local_settings = strjoina(i->image_root, "/", i->local, ".nspawn");
local_settings = strjoin(i->image_root, "/", i->local, ".nspawn");
if (!local_settings)
return log_oom();
r = copy_file_atomic(
i->settings_path,
local_settings,
0664,
COPY_REFLINK |
(FLAGS_SET(i->flags, PULL_FORCE) ? COPY_REPLACE : 0) |
(FLAGS_SET(i->flags, PULL_SYNC) ? COPY_FSYNC_FULL : 0));
if (FLAGS_SET(i->flags, IMPORT_PULL_KEEP_DOWNLOAD))
r = copy_file_atomic(
i->settings_path,
local_settings,
0664,
COPY_REFLINK |
(FLAGS_SET(i->flags, IMPORT_FORCE) ? COPY_REPLACE : 0) |
(FLAGS_SET(i->flags, IMPORT_SYNC) ? COPY_FSYNC_FULL : 0));
else
r = install_file(AT_FDCWD, i->settings_path,
AT_FDCWD, local_settings,
(i->flags & IMPORT_FORCE ? INSTALL_REPLACE : 0) |
(i->flags & IMPORT_SYNC ? INSTALL_SYNCFS : 0));
if (r == -EEXIST)
log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings);
else if (r == -ENOENT)
log_debug_errno(r, "Skipping creation of settings file, since none was found.");
else if (r < 0)
log_warning_errno(r, "Failed to copy settings files %s, ignoring: %m", local_settings);
log_warning_errno(r, "Failed to install settings files %s, ignoring: %m", local_settings);
else
log_info("Created new settings file %s.", local_settings);
}
@ -392,7 +409,7 @@ static void tar_pull_job_on_finished(PullJob *j) {
goto finish;
}
if (i->flags & PULL_DIRECT) {
if (i->flags & IMPORT_DIRECT) {
assert(!i->settings_job);
assert(i->local);
assert(!i->temp_path);
@ -406,8 +423,8 @@ static void tar_pull_job_on_finished(PullJob *j) {
r = install_file(
AT_FDCWD, i->local,
AT_FDCWD, NULL,
(i->flags & PULL_READ_ONLY) ? INSTALL_READ_ONLY : 0 |
(i->flags & PULL_SYNC ? INSTALL_SYNCFS : 0));
(i->flags & IMPORT_READ_ONLY) ? INSTALL_READ_ONLY : 0 |
(i->flags & IMPORT_SYNC ? INSTALL_SYNCFS : 0));
if (r < 0) {
log_error_errno(r, "Failed to finalize '%s': %m", i->local);
goto finish;
@ -432,8 +449,8 @@ static void tar_pull_job_on_finished(PullJob *j) {
r = install_file(
AT_FDCWD, i->temp_path,
AT_FDCWD, i->final_path,
INSTALL_READ_ONLY|
(i->flags & PULL_SYNC ? INSTALL_SYNCFS : 0));
(i->flags & IMPORT_PULL_KEEP_DOWNLOAD ? INSTALL_READ_ONLY : 0) |
(i->flags & IMPORT_SYNC ? INSTALL_SYNCFS : 0));
if (r < 0) {
log_error_errno(r, "Failed to rename to final image name to %s: %m", i->final_path);
goto finish;
@ -460,7 +477,7 @@ static void tar_pull_job_on_finished(PullJob *j) {
AT_FDCWD, i->settings_temp_path,
AT_FDCWD, i->settings_path,
INSTALL_READ_ONLY|
(i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
(i->flags & IMPORT_SYNC ? INSTALL_FSYNC_FULL : 0));
if (r < 0) {
log_error_errno(r, "Failed to rename settings file to %s: %m", i->settings_path);
goto finish;
@ -498,7 +515,7 @@ static int tar_pull_job_on_open_disk_tar(PullJob *j) {
assert(i->tar_job == j);
assert(i->tar_pid <= 0);
if (i->flags & PULL_DIRECT)
if (i->flags & IMPORT_DIRECT)
where = i->local;
else {
if (!i->temp_path) {
@ -512,20 +529,20 @@ static int tar_pull_job_on_open_disk_tar(PullJob *j) {
(void) mkdir_parents_label(where, 0700);
if (FLAGS_SET(i->flags, PULL_DIRECT|PULL_FORCE))
if (FLAGS_SET(i->flags, IMPORT_DIRECT|IMPORT_FORCE))
(void) rm_rf(where, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
if (i->flags & PULL_BTRFS_SUBVOL)
if (i->flags & IMPORT_BTRFS_SUBVOL)
r = btrfs_subvol_make_fallback(AT_FDCWD, where, 0755);
else
r = RET_NERRNO(mkdir(where, 0755));
if (r == -EEXIST && (i->flags & PULL_DIRECT)) /* EEXIST is OK if in direct mode, but not otherwise,
if (r == -EEXIST && (i->flags & IMPORT_DIRECT)) /* EEXIST is OK if in direct mode, but not otherwise,
* because in that case our temporary path collided */
r = 0;
if (r < 0)
return log_error_errno(r, "Failed to create directory/subvolume %s: %m", where);
if (r > 0 && (i->flags & PULL_BTRFS_QUOTA)) { /* actually btrfs subvol */
if (!(i->flags & PULL_DIRECT))
if (r > 0 && (i->flags & IMPORT_BTRFS_QUOTA)) { /* actually btrfs subvol */
if (!(i->flags & IMPORT_DIRECT))
(void) import_assign_pool_quota_and_warn(i->image_root);
(void) import_assign_pool_quota_and_warn(where);
}
@ -577,7 +594,7 @@ int tar_pull_start(
TarPull *i,
const char *url,
const char *local,
PullFlags flags,
ImportFlags flags,
ImportVerify verify,
const char *checksum) {
@ -587,9 +604,9 @@ int tar_pull_start(
assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX);
assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0);
assert((verify < 0) || !checksum);
assert(!(flags & ~PULL_FLAGS_MASK_TAR));
assert(!(flags & PULL_SETTINGS) || !(flags & PULL_DIRECT));
assert(!(flags & PULL_SETTINGS) || !checksum);
assert(!(flags & ~IMPORT_PULL_FLAGS_MASK_TAR));
assert(!(flags & IMPORT_PULL_SETTINGS) || !(flags & IMPORT_DIRECT));
assert(!(flags & IMPORT_PULL_SETTINGS) || !checksum);
if (!http_url_is_valid(url) && !file_url_is_valid(url))
return -EINVAL;
@ -620,7 +637,7 @@ int tar_pull_start(
i->tar_job->on_open_disk = tar_pull_job_on_open_disk_tar;
i->tar_job->calc_checksum = checksum || IN_SET(verify, IMPORT_VERIFY_CHECKSUM, IMPORT_VERIFY_SIGNATURE);
if (!FLAGS_SET(flags, PULL_DIRECT)) {
if (!FLAGS_SET(flags, IMPORT_DIRECT)) {
r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
if (r < 0)
return r;
@ -640,7 +657,7 @@ int tar_pull_start(
return r;
/* Set up download job for the settings file (.nspawn) */
if (FLAGS_SET(flags, PULL_SETTINGS)) {
if (FLAGS_SET(flags, IMPORT_PULL_SETTINGS)) {
r = pull_make_auxiliary_job(
&i->settings_job,
url,
@ -666,7 +683,7 @@ int tar_pull_start(
continue;
j->on_progress = tar_pull_job_on_progress;
j->sync = FLAGS_SET(flags, PULL_SYNC);
j->sync = FLAGS_SET(flags, IMPORT_SYNC);
r = pull_job_begin(j);
if (r < 0)

View file

@ -16,4 +16,4 @@ TarPull* tar_pull_unref(TarPull *pull);
DEFINE_TRIVIAL_CLEANUP_FUNC(TarPull*, tar_pull_unref);
int tar_pull_start(TarPull *pull, const char *url, const char *local, PullFlags flags, ImportVerify verify, const char *checksum);
int tar_pull_start(TarPull *pull, const char *url, const char *local, ImportFlags flags, ImportVerify verify, const char *checksum);

View file

@ -26,11 +26,12 @@
#include "verbs.h"
#include "web-util.h"
static const char *arg_image_root = "/var/lib/machines";
static const char *arg_image_root = NULL;
static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
static PullFlags arg_pull_flags = PULL_SETTINGS | PULL_ROOTHASH | PULL_ROOTHASH_SIGNATURE | PULL_VERITY | PULL_BTRFS_SUBVOL | PULL_BTRFS_QUOTA | PULL_CONVERT_QCOW2 | PULL_SYNC;
static ImportFlags arg_import_flags = IMPORT_PULL_SETTINGS | IMPORT_PULL_ROOTHASH | IMPORT_PULL_ROOTHASH_SIGNATURE | IMPORT_PULL_VERITY | IMPORT_BTRFS_SUBVOL | IMPORT_BTRFS_QUOTA | IMPORT_CONVERT_QCOW2 | IMPORT_SYNC;
static uint64_t arg_offset = UINT64_MAX, arg_size_max = UINT64_MAX;
static char *arg_checksum = NULL;
static ImageClass arg_class = IMAGE_MACHINE;
STATIC_DESTRUCTOR_REGISTER(arg_checksum, freep);
@ -38,7 +39,7 @@ static int normalize_local(const char *local, const char *url, char **ret) {
_cleanup_free_ char *ll = NULL;
int r;
if (arg_pull_flags & PULL_DIRECT) {
if (arg_import_flags & IMPORT_DIRECT) {
if (!local)
log_debug("Writing downloaded data to STDOUT.");
@ -58,13 +59,13 @@ static int normalize_local(const char *local, const char *url, char **ret) {
} else if (local) {
if (!hostname_is_valid(local, 0))
if (!image_name_is_valid(local))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local image name '%s' is not valid.",
local);
if (!FLAGS_SET(arg_pull_flags, PULL_FORCE)) {
r = image_find(IMAGE_MACHINE, local, NULL, NULL);
if (!FLAGS_SET(arg_import_flags, IMPORT_FORCE)) {
r = image_find(arg_class, local, NULL, NULL);
if (r < 0) {
if (r != -ENOENT)
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
@ -89,6 +90,12 @@ static int normalize_local(const char *local, const char *url, char **ret) {
} else
log_info("Pulling '%s'.", url);
if (!FLAGS_SET(arg_import_flags, IMPORT_DIRECT))
log_info("Operating on image directory '%s'.", arg_image_root);
if (!FLAGS_SET(arg_import_flags, IMPORT_SYNC))
log_info("File system synchronization on completion is off.");
*ret = TAKE_PTR(ll);
return 0;
}
@ -130,7 +137,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) {
local = ll;
}
if (!local && FLAGS_SET(arg_pull_flags, PULL_DIRECT))
if (!local && FLAGS_SET(arg_import_flags, IMPORT_DIRECT))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Pulling tar images to STDOUT is not supported.");
r = normalize_local(local, url, &normalized);
@ -141,9 +148,6 @@ static int pull_tar(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
if (!FLAGS_SET(arg_pull_flags, PULL_SYNC))
log_info("File system synchronization on completion is off.");
r = tar_pull_new(&pull, event, arg_image_root, on_tar_finished, event);
if (r < 0)
return log_error_errno(r, "Failed to allocate puller: %m");
@ -152,7 +156,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) {
pull,
url,
normalized,
arg_pull_flags & PULL_FLAGS_MASK_TAR,
arg_import_flags & IMPORT_PULL_FLAGS_MASK_TAR,
arg_verify,
arg_checksum);
if (r < 0)
@ -211,9 +215,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
if (!FLAGS_SET(arg_pull_flags, PULL_SYNC))
log_info("File system synchronization on completion is off.");
r = raw_pull_new(&pull, event, arg_image_root, on_raw_finished, event);
r = raw_pull_new(&pull, event, arg_image_root, on_raw_finished, event);
if (r < 0)
return log_error_errno(r, "Failed to allocate puller: %m");
@ -223,7 +225,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
normalized,
arg_offset,
arg_size_max,
arg_pull_flags & PULL_FLAGS_MASK_RAW,
arg_import_flags & IMPORT_PULL_FLAGS_MASK_RAW,
arg_verify,
arg_checksum);
if (r < 0)
@ -240,7 +242,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
static int help(int argc, char *argv[], void *userdata) {
printf("%1$s [OPTIONS...] {COMMAND} ...\n"
"\n%4$sDownload container or virtual machine images.%5$s\n"
"\n%4$sDownload disk images.%5$s\n"
"\n%2$sCommands:%3$s\n"
" tar URL [NAME] Download a TAR image\n"
" raw URL [NAME] Download a RAW image\n"
@ -255,7 +257,7 @@ static int help(int argc, char *argv[], void *userdata) {
" --roothash-signature=BOOL\n"
" Download root hash signature file with image\n"
" --verity=BOOL Download verity file with image\n"
" --image-root=PATH Image root directory\n\n"
" --image-root=PATH Image root directory\n"
" --read-only Create a read-only image\n"
" --direct Download directly to specified file\n"
" --btrfs-subvol=BOOL Controls whether to create a btrfs subvolume\n"
@ -266,7 +268,11 @@ static int help(int argc, char *argv[], void *userdata) {
" regular disk images\n"
" --sync=BOOL Controls whether to sync() before completing\n"
" --offset=BYTES Offset to seek to in destination\n"
" --size-max=BYTES Maximum number of bytes to write to destination\n",
" --size-max=BYTES Maximum number of bytes to write to destination\n"
" --class=CLASS Select image class (machine, sysext, confext,\n"
" portable)\n"
" --keep-download=BOOL Keep a copy pristine copy of the downloaded file\n"
" around\n",
program_invocation_short_name,
ansi_underline(),
ansi_normal(),
@ -295,6 +301,8 @@ static int parse_argv(int argc, char *argv[]) {
ARG_SYNC,
ARG_OFFSET,
ARG_SIZE_MAX,
ARG_CLASS,
ARG_KEEP_DOWNLOAD,
};
static const struct option options[] = {
@ -315,10 +323,13 @@ static int parse_argv(int argc, char *argv[]) {
{ "sync", required_argument, NULL, ARG_SYNC },
{ "offset", required_argument, NULL, ARG_OFFSET },
{ "size-max", required_argument, NULL, ARG_SIZE_MAX },
{ "class", required_argument, NULL, ARG_CLASS },
{ "keep-download", required_argument, NULL, ARG_KEEP_DOWNLOAD },
{}
};
int c, r;
bool auto_settings = true, auto_keep_download = true;
assert(argc >= 0);
assert(argv);
@ -334,7 +345,7 @@ static int parse_argv(int argc, char *argv[]) {
return version();
case ARG_FORCE:
arg_pull_flags |= PULL_FORCE;
arg_import_flags |= IMPORT_FORCE;
break;
case ARG_IMAGE_ROOT:
@ -366,7 +377,7 @@ static int parse_argv(int argc, char *argv[]) {
return log_oom();
free_and_replace(arg_checksum, hh);
arg_pull_flags &= ~(PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY);
arg_import_flags &= ~(IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY);
arg_verify = _IMPORT_VERIFY_INVALID;
} else
arg_verify = v;
@ -379,7 +390,8 @@ static int parse_argv(int argc, char *argv[]) {
if (r < 0)
return r;
SET_FLAG(arg_pull_flags, PULL_SETTINGS, r);
SET_FLAG(arg_import_flags, IMPORT_PULL_SETTINGS, r);
auto_settings = false;
break;
case ARG_ROOTHASH:
@ -387,11 +399,11 @@ static int parse_argv(int argc, char *argv[]) {
if (r < 0)
return r;
SET_FLAG(arg_pull_flags, PULL_ROOTHASH, r);
SET_FLAG(arg_import_flags, IMPORT_PULL_ROOTHASH, r);
/* If we were asked to turn off the root hash, implicitly also turn off the root hash signature */
if (!r)
SET_FLAG(arg_pull_flags, PULL_ROOTHASH_SIGNATURE, false);
SET_FLAG(arg_import_flags, IMPORT_PULL_ROOTHASH_SIGNATURE, false);
break;
case ARG_ROOTHASH_SIGNATURE:
@ -399,7 +411,7 @@ static int parse_argv(int argc, char *argv[]) {
if (r < 0)
return r;
SET_FLAG(arg_pull_flags, PULL_ROOTHASH_SIGNATURE, r);
SET_FLAG(arg_import_flags, IMPORT_PULL_ROOTHASH_SIGNATURE, r);
break;
case ARG_VERITY:
@ -407,16 +419,16 @@ static int parse_argv(int argc, char *argv[]) {
if (r < 0)
return r;
SET_FLAG(arg_pull_flags, PULL_VERITY, r);
SET_FLAG(arg_import_flags, IMPORT_PULL_VERITY, r);
break;
case ARG_READ_ONLY:
arg_pull_flags |= PULL_READ_ONLY;
arg_import_flags |= IMPORT_READ_ONLY;
break;
case ARG_DIRECT:
arg_pull_flags |= PULL_DIRECT;
arg_pull_flags &= ~(PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY);
arg_import_flags |= IMPORT_DIRECT;
arg_import_flags &= ~(IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY);
break;
case ARG_BTRFS_SUBVOL:
@ -424,7 +436,7 @@ static int parse_argv(int argc, char *argv[]) {
if (r < 0)
return r;
SET_FLAG(arg_pull_flags, PULL_BTRFS_SUBVOL, r);
SET_FLAG(arg_import_flags, IMPORT_BTRFS_SUBVOL, r);
break;
case ARG_BTRFS_QUOTA:
@ -432,7 +444,7 @@ static int parse_argv(int argc, char *argv[]) {
if (r < 0)
return r;
SET_FLAG(arg_pull_flags, PULL_BTRFS_QUOTA, r);
SET_FLAG(arg_import_flags, IMPORT_BTRFS_QUOTA, r);
break;
case ARG_CONVERT_QCOW2:
@ -440,7 +452,7 @@ static int parse_argv(int argc, char *argv[]) {
if (r < 0)
return r;
SET_FLAG(arg_pull_flags, PULL_CONVERT_QCOW2, r);
SET_FLAG(arg_import_flags, IMPORT_CONVERT_QCOW2, r);
break;
case ARG_SYNC:
@ -448,7 +460,7 @@ static int parse_argv(int argc, char *argv[]) {
if (r < 0)
return r;
SET_FLAG(arg_pull_flags, PULL_SYNC, r);
SET_FLAG(arg_import_flags, IMPORT_SYNC, r);
break;
case ARG_OFFSET: {
@ -477,6 +489,22 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
case ARG_CLASS:
arg_class = image_class_from_string(optarg);
if (arg_class < 0)
return log_error_errno(arg_class, "Failed to parse --class= argument: %s", optarg);
break;
case ARG_KEEP_DOWNLOAD:
r = parse_boolean(optarg);
if (r < 0)
return log_error_errno(r, "Failed to parse --keep-download= argument: %s", optarg);
SET_FLAG(arg_import_flags, IMPORT_PULL_KEEP_DOWNLOAD, r);
auto_keep_download = false;
break;
case '?':
return -EINVAL;
@ -490,12 +518,24 @@ static int parse_argv(int argc, char *argv[]) {
!FILE_SIZE_VALID(arg_offset + arg_size_max)))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File offset und maximum size out of range.");
if (arg_offset != UINT64_MAX && !FLAGS_SET(arg_pull_flags, PULL_DIRECT))
if (arg_offset != UINT64_MAX && !FLAGS_SET(arg_import_flags, IMPORT_DIRECT))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File offset only supported in --direct mode.");
if (arg_checksum && (arg_pull_flags & (PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY)) != 0)
if (arg_checksum && (arg_import_flags & (IMPORT_PULL_SETTINGS|IMPORT_PULL_ROOTHASH|IMPORT_PULL_ROOTHASH_SIGNATURE|IMPORT_PULL_VERITY)) != 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Literal checksum verification only supported if no associated files are downloaded.");
if (!arg_image_root)
arg_image_root = image_root_to_string(arg_class);
/* .nspawn settings files only really make sense for machine images, not for sysext/confext/portable */
if (auto_settings && arg_class != IMAGE_MACHINE)
arg_import_flags &= ~IMPORT_PULL_SETTINGS;
/* Keep the original pristine downloaded file as a copy only when dealing with machine images,
* because unlike sysext/confext/portable they are typically modified during runtime. */
if (auto_keep_download)
SET_FLAG(arg_import_flags, IMPORT_PULL_KEEP_DOWNLOAD, arg_class == IMAGE_MACHINE);
return 1;
}
@ -507,19 +547,19 @@ static void parse_env(void) {
r = getenv_bool("SYSTEMD_IMPORT_BTRFS_SUBVOL");
if (r >= 0)
SET_FLAG(arg_pull_flags, PULL_BTRFS_SUBVOL, r);
SET_FLAG(arg_import_flags, IMPORT_BTRFS_SUBVOL, r);
else if (r != -ENXIO)
log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_BTRFS_SUBVOL: %m");
r = getenv_bool("SYSTEMD_IMPORT_BTRFS_QUOTA");
if (r >= 0)
SET_FLAG(arg_pull_flags, PULL_BTRFS_QUOTA, r);
SET_FLAG(arg_import_flags, IMPORT_BTRFS_QUOTA, r);
else if (r != -ENXIO)
log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_BTRFS_QUOTA: %m");
r = getenv_bool("SYSTEMD_IMPORT_SYNC");
if (r >= 0)
SET_FLAG(arg_pull_flags, PULL_SYNC, r);
SET_FLAG(arg_import_flags, IMPORT_SYNC, r);
else if (r != -ENXIO)
log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_SYNC: %m");
}
@ -539,8 +579,7 @@ static int run(int argc, char *argv[]) {
int r;
setlocale(LC_ALL, "");
log_parse_environment();
log_open();
log_setup();
parse_env();

View file

@ -4915,13 +4915,13 @@ _public_ int sd_event_get_state(sd_event *e) {
_public_ int sd_event_get_exit_code(sd_event *e, int *code) {
assert_return(e, -EINVAL);
assert_return(e = event_resolve(e), -ENOPKG);
assert_return(code, -EINVAL);
assert_return(!event_origin_changed(e), -ECHILD);
if (!e->exit_requested)
return -ENODATA;
*code = e->exit_code;
if (code)
*code = e->exit_code;
return 0;
}

View file

@ -15,6 +15,7 @@
#include "alloc-util.h"
#include "build.h"
#include "build-path.h"
#include "bus-common-errors.h"
#include "bus-error.h"
#include "bus-locator.h"
@ -1874,633 +1875,6 @@ static int enable_machine(int argc, char *argv[], void *userdata) {
return 0;
}
static int match_log_message(sd_bus_message *m, void *userdata, sd_bus_error *error) {
const char **our_path = userdata, *line;
unsigned priority;
int r;
assert(m);
assert(our_path);
r = sd_bus_message_read(m, "us", &priority, &line);
if (r < 0) {
bus_log_parse_error(r);
return 0;
}
if (!streq_ptr(*our_path, sd_bus_message_get_path(m)))
return 0;
if (arg_quiet && LOG_PRI(priority) >= LOG_INFO)
return 0;
log_full(priority, "%s", line);
return 0;
}
static int match_transfer_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
const char **our_path = userdata, *path, *result;
uint32_t id;
int r;
assert(m);
assert(our_path);
r = sd_bus_message_read(m, "uos", &id, &path, &result);
if (r < 0) {
bus_log_parse_error(r);
return 0;
}
if (!streq_ptr(*our_path, path))
return 0;
sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), !streq_ptr(result, "done"));
return 0;
}
static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
assert(s);
assert(si);
if (!arg_quiet)
log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32 "\" to abort transfer.", PTR_TO_UINT32(userdata));
sd_event_exit(sd_event_source_get_event(s), EINTR);
return 0;
}
static int transfer_image_common(sd_bus *bus, sd_bus_message *m) {
_cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_event_unrefp) sd_event* event = NULL;
const char *path = NULL;
uint32_t id;
int r;
assert(bus);
assert(m);
polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
r = sd_event_default(&event);
if (r < 0)
return log_error_errno(r, "Failed to get event loop: %m");
r = sd_bus_attach_event(bus, event, 0);
if (r < 0)
return log_error_errno(r, "Failed to attach bus to event loop: %m");
r = bus_match_signal_async(
bus,
&slot_job_removed,
bus_import_mgr,
"TransferRemoved",
match_transfer_removed, NULL, &path);
if (r < 0)
return log_error_errno(r, "Failed to request match: %m");
r = sd_bus_match_signal_async(
bus,
&slot_log_message,
"org.freedesktop.import1",
NULL,
"org.freedesktop.import1.Transfer",
"LogMessage",
match_log_message, NULL, &path);
if (r < 0)
return log_error_errno(r, "Failed to request match: %m");
r = sd_bus_call(bus, m, 0, &error, &reply);
if (r < 0)
return log_error_errno(r, "Failed to transfer image: %s", bus_error_message(&error, r));
r = sd_bus_message_read(reply, "uo", &id, &path);
if (r < 0)
return bus_log_parse_error(r);
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT) >= 0);
if (!arg_quiet)
log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id);
(void) sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id));
(void) sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id));
r = sd_event_loop(event);
if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m");
return -r;
}
static int import_tar(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *ll = NULL, *fn = NULL;
const char *local = NULL, *path = NULL;
_cleanup_close_ int fd = -EBADF;
sd_bus *bus = ASSERT_PTR(userdata);
int r;
if (argc >= 2)
path = empty_or_dash_to_null(argv[1]);
if (argc >= 3)
local = empty_or_dash_to_null(argv[2]);
else if (path) {
r = path_extract_filename(path, &fn);
if (r < 0)
return log_error_errno(r, "Cannot extract container name from filename: %m");
if (r == O_DIRECTORY)
return log_error_errno(SYNTHETIC_ERRNO(EISDIR),
"Path '%s' refers to directory, but we need a regular file: %m", path);
local = fn;
}
if (!local)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Need either path or local name.");
r = tar_strip_suffixes(local, &ll);
if (r < 0)
return log_oom();
local = ll;
if (!hostname_is_valid(local, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local name %s is not a suitable machine name.",
local);
if (path) {
fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return log_error_errno(errno, "Failed to open %s: %m", path);
}
r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ImportTar");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"hsbb",
fd >= 0 ? fd : STDIN_FILENO,
local,
arg_force,
arg_read_only);
if (r < 0)
return bus_log_create_error(r);
return transfer_image_common(bus, m);
}
static int import_raw(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *ll = NULL, *fn = NULL;
const char *local = NULL, *path = NULL;
_cleanup_close_ int fd = -EBADF;
sd_bus *bus = ASSERT_PTR(userdata);
int r;
if (argc >= 2)
path = empty_or_dash_to_null(argv[1]);
if (argc >= 3)
local = empty_or_dash_to_null(argv[2]);
else if (path) {
r = path_extract_filename(path, &fn);
if (r < 0)
return log_error_errno(r, "Cannot extract container name from filename: %m");
if (r == O_DIRECTORY)
return log_error_errno(SYNTHETIC_ERRNO(EISDIR),
"Path '%s' refers to directory, but we need a regular file: %m", path);
local = fn;
}
if (!local)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Need either path or local name.");
r = raw_strip_suffixes(local, &ll);
if (r < 0)
return log_oom();
local = ll;
if (!hostname_is_valid(local, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local name %s is not a suitable machine name.",
local);
if (path) {
fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return log_error_errno(errno, "Failed to open %s: %m", path);
}
r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ImportRaw");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"hsbb",
fd >= 0 ? fd : STDIN_FILENO,
local,
arg_force,
arg_read_only);
if (r < 0)
return bus_log_create_error(r);
return transfer_image_common(bus, m);
}
static int import_fs(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
const char *local = NULL, *path = NULL;
_cleanup_free_ char *fn = NULL;
_cleanup_close_ int fd = -EBADF;
sd_bus *bus = ASSERT_PTR(userdata);
int r;
if (argc >= 2)
path = empty_or_dash_to_null(argv[1]);
if (argc >= 3)
local = empty_or_dash_to_null(argv[2]);
else if (path) {
r = path_extract_filename(path, &fn);
if (r < 0)
return log_error_errno(r, "Cannot extract container name from filename: %m");
local = fn;
}
if (!local)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Need either path or local name.");
if (!hostname_is_valid(local, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local name %s is not a suitable machine name.",
local);
if (path) {
fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
if (fd < 0)
return log_error_errno(errno, "Failed to open directory '%s': %m", path);
}
r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ImportFileSystem");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"hsbb",
fd >= 0 ? fd : STDIN_FILENO,
local,
arg_force,
arg_read_only);
if (r < 0)
return bus_log_create_error(r);
return transfer_image_common(bus, m);
}
static void determine_compression_from_filename(const char *p) {
if (arg_format)
return;
if (!p)
return;
if (endswith(p, ".xz"))
arg_format = "xz";
else if (endswith(p, ".gz"))
arg_format = "gzip";
else if (endswith(p, ".bz2"))
arg_format = "bzip2";
}
static int export_tar(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_close_ int fd = -EBADF;
const char *local = NULL, *path = NULL;
sd_bus *bus = ASSERT_PTR(userdata);
int r;
local = argv[1];
if (!hostname_is_valid(local, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Machine name %s is not valid.", local);
if (argc >= 3)
path = argv[2];
path = empty_or_dash_to_null(path);
if (path) {
determine_compression_from_filename(path);
fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
if (fd < 0)
return log_error_errno(errno, "Failed to open %s: %m", path);
}
r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ExportTar");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"shs",
local,
fd >= 0 ? fd : STDOUT_FILENO,
arg_format);
if (r < 0)
return bus_log_create_error(r);
return transfer_image_common(bus, m);
}
static int export_raw(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_close_ int fd = -EBADF;
const char *local = NULL, *path = NULL;
sd_bus *bus = ASSERT_PTR(userdata);
int r;
local = argv[1];
if (!hostname_is_valid(local, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Machine name %s is not valid.", local);
if (argc >= 3)
path = argv[2];
path = empty_or_dash_to_null(path);
if (path) {
determine_compression_from_filename(path);
fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
if (fd < 0)
return log_error_errno(errno, "Failed to open %s: %m", path);
}
r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ExportRaw");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"shs",
local,
fd >= 0 ? fd : STDOUT_FILENO,
arg_format);
if (r < 0)
return bus_log_create_error(r);
return transfer_image_common(bus, m);
}
static int pull_tar(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *l = NULL, *ll = NULL;
const char *local, *remote;
sd_bus *bus = ASSERT_PTR(userdata);
int r;
remote = argv[1];
if (!http_url_is_valid(remote) && !file_url_is_valid(remote))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"URL '%s' is not valid.", remote);
if (argc >= 3)
local = argv[2];
else {
r = import_url_last_component(remote, &l);
if (r < 0)
return log_error_errno(r, "Failed to get final component of URL: %m");
local = l;
}
local = empty_or_dash_to_null(local);
if (local) {
r = tar_strip_suffixes(local, &ll);
if (r < 0)
return log_oom();
local = ll;
if (!hostname_is_valid(local, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local name %s is not a suitable machine name.",
local);
}
r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullTar");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"sssb",
remote,
local,
import_verify_to_string(arg_verify),
arg_force);
if (r < 0)
return bus_log_create_error(r);
return transfer_image_common(bus, m);
}
static int pull_raw(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *l = NULL, *ll = NULL;
const char *local, *remote;
sd_bus *bus = ASSERT_PTR(userdata);
int r;
remote = argv[1];
if (!http_url_is_valid(remote) && !file_url_is_valid(remote))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"URL '%s' is not valid.", remote);
if (argc >= 3)
local = argv[2];
else {
r = import_url_last_component(remote, &l);
if (r < 0)
return log_error_errno(r, "Failed to get final component of URL: %m");
local = l;
}
local = empty_or_dash_to_null(local);
if (local) {
r = raw_strip_suffixes(local, &ll);
if (r < 0)
return log_oom();
local = ll;
if (!hostname_is_valid(local, 0))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Local name %s is not a suitable machine name.",
local);
}
r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullRaw");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(
m,
"sssb",
remote,
local,
import_verify_to_string(arg_verify),
arg_force);
if (r < 0)
return bus_log_create_error(r);
return transfer_image_common(bus, m);
}
typedef struct TransferInfo {
uint32_t id;
const char *type;
const char *remote;
const char *local;
double progress;
} TransferInfo;
static int compare_transfer_info(const TransferInfo *a, const TransferInfo *b) {
return strcmp(a->local, b->local);
}
static int list_transfers(int argc, char *argv[], void *userdata) {
size_t max_type = STRLEN("TYPE"), max_local = STRLEN("LOCAL"), max_remote = STRLEN("REMOTE");
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ TransferInfo *transfers = NULL;
const char *type, *remote, *local;
sd_bus *bus = userdata;
uint32_t id, max_id = 0;
size_t n_transfers = 0;
double progress;
int r;
pager_open(arg_pager_flags);
r = bus_call_method(bus, bus_import_mgr, "ListTransfers", &error, &reply, NULL);
if (r < 0)
return log_error_errno(r, "Could not get transfers: %s", bus_error_message(&error, r));
r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
if (r < 0)
return bus_log_parse_error(r);
while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, NULL)) > 0) {
size_t l;
if (!GREEDY_REALLOC(transfers, n_transfers + 1))
return log_oom();
transfers[n_transfers].id = id;
transfers[n_transfers].type = type;
transfers[n_transfers].remote = remote;
transfers[n_transfers].local = local;
transfers[n_transfers].progress = progress;
l = strlen(type);
if (l > max_type)
max_type = l;
l = strlen(remote);
if (l > max_remote)
max_remote = l;
l = strlen(local);
if (l > max_local)
max_local = l;
if (id > max_id)
max_id = id;
n_transfers++;
}
if (r < 0)
return bus_log_parse_error(r);
r = sd_bus_message_exit_container(reply);
if (r < 0)
return bus_log_parse_error(r);
typesafe_qsort(transfers, n_transfers, compare_transfer_info);
if (arg_legend && n_transfers > 0)
printf("%-*s %-*s %-*s %-*s %-*s\n",
(int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
(int) 7, "PERCENT",
(int) max_type, "TYPE",
(int) max_local, "LOCAL",
(int) max_remote, "REMOTE");
for (size_t j = 0; j < n_transfers; j++)
if (transfers[j].progress < 0)
printf("%*" PRIu32 " %*s %-*s %-*s %-*s\n",
(int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
(int) 7, "n/a",
(int) max_type, transfers[j].type,
(int) max_local, transfers[j].local,
(int) max_remote, transfers[j].remote);
else
printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
(int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
(int) 6, (unsigned) (transfers[j].progress * 100),
(int) max_type, transfers[j].type,
(int) max_local, transfers[j].local,
(int) max_remote, transfers[j].remote);
if (arg_legend) {
if (n_transfers > 0)
printf("\n%zu transfers listed.\n", n_transfers);
else
printf("No transfers.\n");
}
return 0;
}
static int cancel_transfer(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = ASSERT_PTR(userdata);
int r;
polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
for (int i = 1; i < argc; i++) {
uint32_t id;
r = safe_atou32(argv[i], &id);
if (r < 0)
return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
r = bus_call_method(bus, bus_import_mgr, "CancelTransfer", &error, NULL, "u", id);
if (r < 0)
return log_error_errno(r, "Could not cancel transfer: %s", bus_error_message(&error, r));
}
return 0;
}
static int set_limit(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
@ -2585,6 +1959,52 @@ static int clean_images(int argc, char *argv[], void *userdata) {
return 0;
}
static int chainload_importctl(int argc, char *argv[]) {
int r;
log_notice("The 'machinectl %1$s' command has been replaced by 'importctl -m %1$s'. Redirecting invocation.", argv[optind]);
_cleanup_strv_free_ char **c =
strv_new("importctl", "--class=machine");
if (!c)
return log_oom();
if (FLAGS_SET(arg_pager_flags, PAGER_DISABLE))
if (strv_extend(&c, "--no-pager") < 0)
return log_oom();
if (!arg_legend)
if (strv_extend(&c, "--no-legend") < 0)
return log_oom();
if (arg_read_only)
if (strv_extend(&c, "--read-only") < 0)
return log_oom();
if (arg_force)
if (strv_extend(&c, "--force") < 0)
return log_oom();
if (arg_quiet)
if (strv_extend(&c, "--quiet") < 0)
return log_oom();
if (!arg_ask_password)
if (strv_extend(&c, "--no-ask-password") < 0)
return log_oom();
if (strv_extend_many(&c, "--verify", import_verify_to_string(arg_verify)) < 0)
return log_oom();
if (arg_format)
if (strv_extend_many(&c, "--format", arg_format) < 0)
return log_oom();
if (strv_extend_strv(&c, argv + optind, /* filter_duplicates= */ false) < 0)
return log_oom();
if (DEBUG_LOGGING) {
_cleanup_free_ char *joined = strv_join(c, " ");
log_debug("Chainloading: %s", joined);
}
r = invoke_callout_binary(BINDIR "/importctl", c);
return log_error_errno(r, "Failed to invoke 'importctl': %m");
}
static int help(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *link = NULL;
int r;
@ -2629,16 +2049,6 @@ static int help(int argc, char *argv[], void *userdata) {
" remove NAME... Remove an image\n"
" set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n"
" clean Remove hidden (or all) images\n"
"\n%3$sImage Transfer Commands:%4$s\n"
" pull-tar URL [NAME] Download a TAR container image\n"
" pull-raw URL [NAME] Download a RAW container or VM image\n"
" import-tar FILE [NAME] Import a local TAR container image\n"
" import-raw FILE [NAME] Import a local RAW container or VM image\n"
" import-fs DIRECTORY [NAME] Import a local directory container image\n"
" export-tar NAME [FILE] Export a TAR container image locally\n"
" export-raw NAME [FILE] Export a RAW container or VM image locally\n"
" list-transfers Show list of downloads in progress\n"
" cancel-transfer Cancel a download\n"
"\n%3$sOptions:%4$s\n"
" -h --help Show this help\n"
" --version Show package version\n"
@ -2656,7 +2066,7 @@ static int help(int argc, char *argv[], void *userdata) {
" -s --signal=SIGNAL Which signal to send\n"
" --uid=USER Specify user ID to invoke shell as\n"
" -E --setenv=VAR[=VALUE] Add an environment variable for shell\n"
" --read-only Create read-only bind mount\n"
" --read-only Create read-only bind mount or clone\n"
" --mkdir Create directory before bind mounting, if missing\n"
" -n --lines=INTEGER Number of journal entries to show\n"
" --max-addresses=INTEGER Number of internet addresses to show at most\n"
@ -2665,9 +2075,7 @@ static int help(int argc, char *argv[], void *userdata) {
" short-monotonic, short-unix, short-delta,\n"
" json, json-pretty, json-sse, json-seq, cat,\n"
" verbose, export, with-unit)\n"
" --verify=MODE Verification mode for downloaded images (no,\n"
" checksum, signature)\n"
" --force Download image even if already exists\n"
" --force Replace target file when copying, if necessary\n"
" --now Start or power off container after enabling or\n"
" disabling it\n"
" --runner=RUNNER Select between nspawn and vmspawn as the runner\n"
@ -3007,15 +2415,6 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
{ "start", 2, VERB_ANY, 0, start_machine },
{ "enable", 2, VERB_ANY, 0, enable_machine },
{ "disable", 2, VERB_ANY, 0, enable_machine },
{ "import-tar", 2, 3, 0, import_tar },
{ "import-raw", 2, 3, 0, import_raw },
{ "import-fs", 2, 3, 0, import_fs },
{ "export-tar", 2, 3, 0, export_tar },
{ "export-raw", 2, 3, 0, export_raw },
{ "pull-tar", 2, 3, 0, pull_tar },
{ "pull-raw", 2, 3, 0, pull_raw },
{ "list-transfers", VERB_ANY, 1, 0, list_transfers },
{ "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
{ "set-limit", 2, 3, 0, set_limit },
{ "clean", VERB_ANY, 1, 0, clean_images },
{}
@ -3033,13 +2432,19 @@ static int run(int argc, char *argv[]) {
/* The journal merging logic potentially needs a lot of fds. */
(void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
sigbus_install();
r = parse_argv(argc, argv);
if (r <= 0)
return r;
if (STRPTR_IN_SET(argv[optind],
"import-tar", "import-raw", "import-fs",
"export-tar", "export-raw",
"pull-tar", "pull-raw",
"list-transfers", "cancel-transfer"))
return chainload_importctl(argc, argv);
r = bus_connect_transport(arg_transport, arg_host, RUNTIME_SCOPE_SYSTEM, &bus);
if (r < 0)
return bus_log_connect_error(r, arg_transport);

View file

@ -99,6 +99,15 @@ static const char* image_class_suffix_table[_IMAGE_CLASS_MAX] = {
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(image_class_suffix, ImageClass);
static const char *const image_root_table[_IMAGE_CLASS_MAX] = {
[IMAGE_MACHINE] = "/var/lib/machines",
[IMAGE_PORTABLE] = "/var/lib/portables",
[IMAGE_SYSEXT] = "/var/lib/extensions",
[IMAGE_CONFEXT] = "/var/lib/confexts",
};
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(image_root, ImageClass);
static Image *image_free(Image *i) {
assert(i);

View file

@ -119,4 +119,6 @@ static inline bool IMAGE_IS_HOST(const struct Image *i) {
int image_to_json(const struct Image *i, JsonVariant **ret);
const char *image_root_to_string(ImageClass c) _const_;
extern const struct hash_ops image_hash_ops;

View file

@ -462,3 +462,75 @@ int terminal_tint_color(double hue, char **ret) {
return 0;
}
void draw_progress_bar(const char *prefix, double percentage) {
fputs("\r", stderr);
if (prefix)
fputs(prefix, stderr);
if (!terminal_is_dumb()) {
size_t cols = columns();
size_t prefix_length = strlen_ptr(prefix);
size_t length = cols > prefix_length + 6 ? cols - prefix_length - 6 : 0;
fputs(ansi_highlight_green(), stderr);
if (length > 5 && percentage >= 0.0 && percentage <= 100.0) {
size_t p = (size_t) (length * percentage / 100.0);
bool separator_done = false;
for (size_t i = 0; i < length; i++) {
if (i <= p) {
if (get_color_mode() == COLOR_24BIT) {
uint8_t r8, g8, b8;
double z = i == 0 ? 0 : (((double) i / p) * 100);
hsv_to_rgb(145 /* green */, z, 33 + z*2/3, &r8, &g8, &b8);
fprintf(stderr, "\x1B[38;2;%u;%u;%um", r8, g8, b8);
}
fputs(special_glyph(SPECIAL_GLYPH_HORIZONTAL_FAT), stderr);
} else if (i+1 < length && !separator_done) {
fputs(ansi_normal(), stderr);
fputc(' ', stderr);
separator_done = true;
fputs(ansi_grey(), stderr);
} else
fputs(special_glyph(SPECIAL_GLYPH_HORIZONTAL_DOTTED), stderr);
}
fputs(ansi_normal(), stderr);
fputc(' ', stderr);
}
}
fprintf(stderr,
"%s%3.0f%%%s",
ansi_highlight(),
percentage,
ansi_normal());
if (!terminal_is_dumb())
fputs(ANSI_ERASE_TO_END_OF_LINE, stderr);
fputc('\r', stderr);
fflush(stderr);
}
void clear_progress_bar(const char *prefix) {
fputc('\r', stderr);
if (terminal_is_dumb()) {
size_t l = strlen_ptr(prefix);
for (size_t i = 0; i < l; i ++)
fputc(' ', stderr);
fputs(" ", stderr);
} else
fputs(ANSI_ERASE_TO_END_OF_LINE, stderr);
fputc('\r', stderr);
fflush(stderr);
}

View file

@ -49,3 +49,6 @@ static inline const char *green_check_mark_internal(char buffer[static GREEN_CHE
#define COLOR_MARK_BOOL(b) ((b) ? GREEN_CHECK_MARK() : RED_CROSS_MARK())
int terminal_tint_color(double hue, char **ret);
void draw_progress_bar(const char *prefix, double percentage);
void clear_progress_bar(const char *prefix);

View file

@ -380,6 +380,10 @@ executables += [
'sources' : files('test-process-util.c'),
'dependencies' : threads,
},
test_template + {
'sources' : files('test-progress-bar.c'),
'type' : 'manual',
},
test_template + {
'sources' : files('test-qrcode-util.c'),
'dependencies' : libdl,

View file

@ -92,6 +92,8 @@ TEST(dump_special_glyphs) {
dump_glyph(SPECIAL_GLYPH_TREE_SPACE);
dump_glyph(SPECIAL_GLYPH_TREE_TOP);
dump_glyph(SPECIAL_GLYPH_VERTICAL_DOTTED);
dump_glyph(SPECIAL_GLYPH_HORIZONTAL_DOTTED);
dump_glyph(SPECIAL_GLYPH_HORIZONTAL_FAT);
dump_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET);
dump_glyph(SPECIAL_GLYPH_BLACK_CIRCLE);
dump_glyph(SPECIAL_GLYPH_WHITE_CIRCLE);

View file

@ -0,0 +1,34 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "pretty-print.h"
#include "random-util.h"
#include "tests.h"
#define PROGRESS_PREFIX "test: "
TEST(progress_bar) {
draw_progress_bar(PROGRESS_PREFIX, 0);
bool paused = false;
for (double d = 0; d <= 100; d += 0.5) {
usleep_safe(random_u64_range(20 * USEC_PER_MSEC));
draw_progress_bar(PROGRESS_PREFIX, d);
if (!paused && d >= 50) {
clear_progress_bar(PROGRESS_PREFIX);
fputs("Sleeping for 1s...", stdout);
fflush(stdout);
usleep_safe(USEC_PER_SEC);
paused = true;
}
}
draw_progress_bar(PROGRESS_PREFIX, 100);
usleep_safe(300 * MSEC_PER_SEC);
clear_progress_bar(PROGRESS_PREFIX);
fputs("Done.\n", stdout);
}
DEFINE_TEST_MAIN(LOG_INFO);

View file

@ -0,0 +1,66 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
# shellcheck disable=SC2016
set -eux
set -o pipefail
# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh
export PAGER=
at_exit() {
set +e
umount -l -R /var/lib/confexts
rm -f /var/tmp/importtest /var/tmp/importtest2 /var/tmp/importtest.tar.gz /var/tmp/importtest2.tar.gz
}
trap at_exit EXIT
systemctl service-log-level systemd-importd debug
# Mount tmpfs over /var/lib/confexts to not pollute the image
mkdir -p /var/lib/confexts
mount -t tmpfs tmpfs /var/lib/confexts -o mode=755
importctl
importctl --no-pager --help
importctl --version
importctl list-transfers
importctl list-transfers --no-legend --no-ask-password
importctl list-transfers -j
importctl list-images
importctl list-images --no-legend --no-ask-password
importctl list-images -j
(! importctl cancel-transfer 4711)
dd if=/dev/urandom of=/var/tmp/importtest bs=4096 count=10
importctl import-raw --class=confext /var/tmp/importtest
cmp /var/tmp/importtest /var/lib/confexts/importtest.raw
importctl export-raw --class=confext importtest /var/tmp/importtest2
cmp /var/tmp/importtest /var/tmp/importtest2
(! importctl pull-raw --class=confext file:///var/tmp/importtest)
importctl pull-raw --verify=no --class=confext file:///var/tmp/importtest importtest3
cmp /var/tmp/importtest /var/lib/confexts/importtest3.raw
tar czf /var/tmp/importtest.tar.gz -C /var/tmp importtest
importctl import-tar --class=confext /var/tmp/importtest.tar.gz importtest4
cmp /var/tmp/importtest /var/lib/confexts/importtest4/importtest
importctl export-tar --class=confext importtest4 /var/tmp/importtest2.tar.gz
importctl import-tar --class=confext /var/tmp/importtest2.tar.gz importtest5
cmp /var/tmp/importtest /var/lib/confexts/importtest5/importtest
importctl import-fs --class=confext /var/lib/confexts/importtest5 importtest6
cmp /var/tmp/importtest /var/lib/confexts/importtest6/importtest
(! importctl pull-tar --class=confext file:///var/tmp/importtest.tar.gz importtest7)
importctl pull-tar --class=confext --verify=no file:///var/tmp/importtest.tar.gz importtest7
cmp /var/tmp/importtest /var/lib/confexts/importtest7/importtest
importctl list-images
importctl list-images -j

View file

@ -20,6 +20,9 @@ at_exit() {
trap at_exit EXIT
systemctl service-log-level systemd-machined debug
systemctl service-log-level systemd-importd debug
# Mount tmpfs over /var/lib/machines to not pollute the image
mkdir -p /var/lib/machines
mount -t tmpfs tmpfs /var/lib/machines