repart: varlink support (#39421)

Split out of #38764

Replaces: #33741
This commit is contained in:
Lennart Poettering
2025-11-01 23:21:37 +01:00
committed by GitHub
12 changed files with 1374 additions and 189 deletions

View File

@@ -828,6 +828,29 @@ manpages = [
'sd_journal_seek_tail'],
''],
['sd_journal_stream_fd', '3', ['sd_journal_stream_fd_with_namespace'], ''],
['sd_json_dispatch_string',
'3',
['sd_json_dispatch_const_string',
'sd_json_dispatch_double',
'sd_json_dispatch_id128',
'sd_json_dispatch_int16',
'sd_json_dispatch_int32',
'sd_json_dispatch_int64',
'sd_json_dispatch_int8',
'sd_json_dispatch_intbool',
'sd_json_dispatch_signal',
'sd_json_dispatch_stdbool',
'sd_json_dispatch_strv',
'sd_json_dispatch_tristate',
'sd_json_dispatch_uid_gid',
'sd_json_dispatch_uint16',
'sd_json_dispatch_uint32',
'sd_json_dispatch_uint64',
'sd_json_dispatch_uint8',
'sd_json_dispatch_unsupported',
'sd_json_dispatch_variant',
'sd_json_dispatch_variant_noref'],
''],
['sd_listen_fds',
'3',
['SD_LISTEN_FDS_START', 'sd_listen_fds_with_names'],

View File

@@ -0,0 +1,364 @@
<?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">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="sd_json_dispatch_string" xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>sd_json_dispatch_string</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>sd_json_dispatch_string</refentrytitle>
<manvolnum>3</manvolnum>
</refmeta>
<refnamediv>
<refname>sd_json_dispatch_string</refname>
<refname>sd_json_dispatch_const_string</refname>
<refname>sd_json_dispatch_strv</refname>
<refname>sd_json_dispatch_stdbool</refname>
<refname>sd_json_dispatch_intbool</refname>
<refname>sd_json_dispatch_tristate</refname>
<refname>sd_json_dispatch_variant</refname>
<refname>sd_json_dispatch_variant_noref</refname>
<refname>sd_json_dispatch_int64</refname>
<refname>sd_json_dispatch_int32</refname>
<refname>sd_json_dispatch_int16</refname>
<refname>sd_json_dispatch_int8</refname>
<refname>sd_json_dispatch_uint64</refname>
<refname>sd_json_dispatch_uint32</refname>
<refname>sd_json_dispatch_uint16</refname>
<refname>sd_json_dispatch_uint8</refname>
<refname>sd_json_dispatch_double</refname>
<refname>sd_json_dispatch_uid_gid</refname>
<refname>sd_json_dispatch_id128</refname>
<refname>sd_json_dispatch_signal</refname>
<refname>sd_json_dispatch_unsupported</refname>
<refpurpose>Decode JSON variant values and write them to the specified memory</refpurpose>
</refnamediv>
<refsynopsisdiv>
<funcsynopsis>
<funcsynopsisinfo>#include &lt;systemd/sd-varlink.h&gt;</funcsynopsisinfo>
<funcprototype>
<funcdef>int <function>sd_json_dispatch_string</function></funcdef>
<paramdef>const char *<parameter>name</parameter></paramdef>
<paramdef>sd_json_variant *<parameter>variant</parameter></paramdef>
<paramdef>sd_dispatch_flags <parameter>flags</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_json_dispatch_const_string</function></funcdef>
<paramdef>const char *<parameter>name</parameter></paramdef>
<paramdef>sd_json_variant *<parameter>variant</parameter></paramdef>
<paramdef>sd_dispatch_flags <parameter>flags</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_json_dispatch_strv</function></funcdef>
<paramdef>const char *<parameter>name</parameter></paramdef>
<paramdef>sd_json_variant *<parameter>variant</parameter></paramdef>
<paramdef>sd_dispatch_flags <parameter>flags</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_json_dispatch_stdbool</function></funcdef>
<paramdef>const char *<parameter>name</parameter></paramdef>
<paramdef>sd_json_variant *<parameter>variant</parameter></paramdef>
<paramdef>sd_dispatch_flags <parameter>flags</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_json_dispatch_intbool</function></funcdef>
<paramdef>const char *<parameter>name</parameter></paramdef>
<paramdef>sd_json_variant *<parameter>variant</parameter></paramdef>
<paramdef>sd_dispatch_flags <parameter>flags</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_json_dispatch_tristate</function></funcdef>
<paramdef>const char *<parameter>name</parameter></paramdef>
<paramdef>sd_json_variant *<parameter>variant</parameter></paramdef>
<paramdef>sd_dispatch_flags <parameter>flags</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_json_dispatch_variant</function></funcdef>
<paramdef>const char *<parameter>name</parameter></paramdef>
<paramdef>sd_json_variant *<parameter>variant</parameter></paramdef>
<paramdef>sd_dispatch_flags <parameter>flags</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_json_dispatch_variant_noref</function></funcdef>
<paramdef>const char *<parameter>name</parameter></paramdef>
<paramdef>sd_json_variant *<parameter>variant</parameter></paramdef>
<paramdef>sd_dispatch_flags <parameter>flags</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_json_dispatch_int64</function></funcdef>
<paramdef>const char *<parameter>name</parameter></paramdef>
<paramdef>sd_json_variant *<parameter>variant</parameter></paramdef>
<paramdef>sd_dispatch_flags <parameter>flags</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_json_dispatch_int32</function></funcdef>
<paramdef>const char *<parameter>name</parameter></paramdef>
<paramdef>sd_json_variant *<parameter>variant</parameter></paramdef>
<paramdef>sd_dispatch_flags <parameter>flags</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_json_dispatch_int16</function></funcdef>
<paramdef>const char *<parameter>name</parameter></paramdef>
<paramdef>sd_json_variant *<parameter>variant</parameter></paramdef>
<paramdef>sd_dispatch_flags <parameter>flags</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_json_dispatch_int8</function></funcdef>
<paramdef>const char *<parameter>name</parameter></paramdef>
<paramdef>sd_json_variant *<parameter>variant</parameter></paramdef>
<paramdef>sd_dispatch_flags <parameter>flags</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_json_dispatch_uint64</function></funcdef>
<paramdef>const char *<parameter>name</parameter></paramdef>
<paramdef>sd_json_variant *<parameter>variant</parameter></paramdef>
<paramdef>sd_dispatch_flags <parameter>flags</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_json_dispatch_uint32</function></funcdef>
<paramdef>const char *<parameter>name</parameter></paramdef>
<paramdef>sd_json_variant *<parameter>variant</parameter></paramdef>
<paramdef>sd_dispatch_flags <parameter>flags</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_json_dispatch_uint16</function></funcdef>
<paramdef>const char *<parameter>name</parameter></paramdef>
<paramdef>sd_json_variant *<parameter>variant</parameter></paramdef>
<paramdef>sd_dispatch_flags <parameter>flags</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_json_dispatch_uint8</function></funcdef>
<paramdef>const char *<parameter>name</parameter></paramdef>
<paramdef>sd_json_variant *<parameter>variant</parameter></paramdef>
<paramdef>sd_dispatch_flags <parameter>flags</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_json_dispatch_double</function></funcdef>
<paramdef>const char *<parameter>name</parameter></paramdef>
<paramdef>sd_json_variant *<parameter>variant</parameter></paramdef>
<paramdef>sd_dispatch_flags <parameter>flags</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_json_dispatch_uid_gid</function></funcdef>
<paramdef>const char *<parameter>name</parameter></paramdef>
<paramdef>sd_json_variant *<parameter>variant</parameter></paramdef>
<paramdef>sd_dispatch_flags <parameter>flags</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_json_dispatch_id128</function></funcdef>
<paramdef>const char *<parameter>name</parameter></paramdef>
<paramdef>sd_json_variant *<parameter>variant</parameter></paramdef>
<paramdef>sd_dispatch_flags <parameter>flags</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_json_dispatch_signal</function></funcdef>
<paramdef>const char *<parameter>name</parameter></paramdef>
<paramdef>sd_json_variant *<parameter>variant</parameter></paramdef>
<paramdef>sd_dispatch_flags <parameter>flags</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_json_dispatch_unsupported</function></funcdef>
<paramdef>const char *<parameter>name</parameter></paramdef>
<paramdef>sd_json_variant *<parameter>variant</parameter></paramdef>
<paramdef>sd_dispatch_flags <parameter>flags</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>The various functions described here are intended for use in the
<type>sd_json_dispatch_field</type> structure arrays the
<citerefentry><refentrytitle>sd_json_dispatch</refentrytitle><manvolnum>3</manvolnum></citerefentry> and
<citerefentry><refentrytitle>sd_varlink_dispatch</refentrytitle><manvolnum>3</manvolnum></citerefentry>
functions accept; they decode the provided JSON variant object's value, and write it to the memory
indicated by the <parameter>userdata</parameter> pointer. The <parameter>name</parameter> parameter
contains the field name (in the JSON object it is contained in) of the value being decoded. For details
on the <parameter>flags</parameter> parameter see the <function>sd_json_dispatch()</function>
documentation.</para>
<para>Note that all these functions not only accept the native JSON type they are intended for, but also
accept null JSON values, in which case they assign an appropriate invalid/unset/null value, as
appropriate for the type (for details see below).</para>
<para><function>sd_json_dispatch_string()</function> decodes a JSON string value, and allocates a
<constant>NUL</constant> terminated copy in dynamic memory. The <parameter>userdata</parameter> pointer
must point to a pointer to a string, which is freed if non-<constant>NULL</constant>, and then replaced
by the newly allocated one. If a JSON null value is passed, the existing string is freed and
<constant>NULL</constant> is assigned.</para>
<para><function>sd_json_dispatch_const_string()</function> is very similar to
<function>sd_json_dispatch_string()</function>, but does not allocate a string in dynamic
memory. Instead, it just writes a pointer into the JSON object into the indicated memory (or
<constant>NULL</constant> in case a JSON null object is passed). The memory remains valid only as long as
the indicated variant object is kept allocated (which can happen via direct reference, or via an indirect
one via an object that references the specified variant). The memory <parameter>userdata</parameter>
points to on input is <emphasis>not</emphasis> freed before the new value is assigned.</para>
<para><function>sd_json_dispatch_stdbool()</function> and <function>sd_json_dispatch_intbool()</function>
decode JSON boolean values and write them to the indicated memory. The former expects a variable of the
C99 <type>bool</type> type in the indicated memory, the latter an <type>int</type> (which will only
receive the values 0 and 1). The JSON null value is treated equivalent to a JSON false.</para>
<para><function>sd_json_dispatch_tristate()</function> is very similar
to<function>sd_json_dispatch_intbool()</function>, but will assign -1 if a JSON null value is passed. Or
in other words, the integer will have a value &gt; 0, == 0 or &lt; 0, for the cases true, false or
invalid/unset/null.</para>
<para><function>sd_json_dispatch_variant()</function> takes an additional reference to the passed JSON
object (via <function>sd_json_variant_ref()</function>) and writes the pointer to the indicated
memory. No decoding is done. If the indicated pointer is non-<constant>NULL</constant> on input it is
freed (via <function>sd_json_variant_unref()</function>) before the new pointer is written.</para>
<para><function>sd_json_dispatch_variant_noref()</function> is similar, but does <emphasis>not</emphasis>
take a new reference to the JSON variant object. The pointer hence only remains valid as long as the
original object stays referenced. If the indicated pointer is non-<constant>NULL</constant> on input it
is <emphasis>not</emphasis> freed before the new pointer is written.</para>
<para>The <function>sd_json_dispatch_int64()</function>, <function>sd_json_dispatch_int32()</function>,
<function>sd_json_dispatch_int16()</function>, <function>sd_json_dispatch_int8()</function>,
<function>sd_json_dispatch_uint64()</function>, <function>sd_json_dispatch_uint32()</function>,
<function>sd_json_dispatch_uint16()</function> and <function>sd_json_dispatch_uint8()</function>
functions decode a JSON integer value, and write the value to the indicated memory. The function names
indicate the word width and signedness of the integers being parsed. If the JSON null value is passed the
functions for the unsigned integer types will assign the maximum value the type takes
(i.e. <constant>UINT64_MAX</constant>, <constant>UINT32_MAX</constant> …), and the signed versions assign
-1. Instead of a JSON integer value these functions also accept JSON strings that contain formatted
decimal numbers, in order to improve compatibility for encoding integer values that cannot be represented
in 64bit double precision floating point numbers in other programming languages that encode JSON numerals
this way.</para>
<para>The <function>sd_json_dispatch_double()</function> function decodes a 64bit double precision
floating point number. If a JSON null value is passed, assigns NaN.</para>
<para>The <function>sd_json_dispatch_uid_gid()</function> function is similar to
<function>sd_json_dispatch_uint32()</function>, and is intended to decode 32bit UNIX UID/GID numbers, as
used on Linux. It will decode a JSON null value as 4294967295 (i.e. <literal>(uid_t) -1</literal>), and
will refuse the values 65535 and 4294967295 when passed as JSON numerals (i.e. both the 16bit and 32bit
"invalid" UID/GID, as these values have special meaning for various UNIX syscalls, on different OSes and
file systems).</para>
<para><function>sd_json_dispatch_id128()</function> decodes a 128bit ID formatted as a JSON string. It
supports both RFC9562 UUID formatting, as well as 64 hexadecimal characters without separators, the same
way as
<citerefentry><refentrytitle>sd_id128_from_string</refentrytitle><manvolnum>3</manvolnum></citerefentry>. If
the JSON null value is passed, the all-zero ID is assigned.</para>
<para><function>sd_json_dispatch_signal()</function> decodes a UNIX process signal specification. It
expects either an JSON string containing a signal name such as <literal>SIGINT</literal> or
<literal>SIGTERM</literal>, or an unsigned JSON integer value with the signal number (in the Linux
definition). The indicated memory must point to an <type>int</type> variable to write the signal number
to. If the JSON null value is passed a negative value will be written to the memory.</para>
<para><function>sd_json_dispatch_unsupported()</function> will always fail with the
-<constant>EINVAL</constant> error.</para>
</refsect1>
<refsect1>
<title>Return Value</title>
<para>On success, these functions return a non-negative integer. On failure, they return a negative
errno-style error code.</para>
<refsect2>
<title>Errors</title>
<para>Returned errors may indicate the following problems:</para>
<variablelist>
<varlistentry>
<term><constant>-EINVAL</constant></term>
<listitem><para>An argument is invalid.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-ENOMEM</constant></term>
<listitem><para>Memory allocation failed.</para></listitem>
</varlistentry>
</variablelist>
</refsect2>
</refsect1>
<xi:include href="libsystemd-pkgconfig.xml" />
<refsect1>
<title>History</title>
<para>
<function>sd_json_dispatch_string()</function>, <function>sd_json_dispatch_const_string()</function>,
<function>sd_json_dispatch_strv()</function>, <function>sd_json_dispatch_stdbool()</function>,
<function>sd_json_dispatch_intbool()</function>, <function>sd_json_dispatch_tristate()</function>,
<function>sd_json_dispatch_variant()</function>, <function>sd_json_dispatch_variant_noref()</function>,
<function>sd_json_dispatch_int64()</function>, <function>sd_json_dispatch_int32()</function>,
<function>sd_json_dispatch_int16()</function>, <function>sd_json_dispatch_int8()</function>,
<function>sd_json_dispatch_uint64()</function>, <function>sd_json_dispatch_uint32()</function>,
<function>sd_json_dispatch_uint16()</function>, <function>sd_json_dispatch_uint8()</function>,
<function>sd_json_dispatch_double()</function>, <function>sd_json_dispatch_uid_gid()</function>,
<function>sd_json_dispatch_id128()</function>, <function>sd_json_dispatch_signal()</function>,
<function>sd_json_dispatch_unsupported()</function> were added in version 257.</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sd-json</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sd-varlink</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sd_json_dispatch</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>sd_variant_dispatch</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>
</refentry>

View File

@@ -26,7 +26,7 @@
<cmdsynopsis>
<command>systemd-repart</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="opt" rep="repeat"><replaceable><optional>BLOCKDEVICE</optional></replaceable></arg>
<arg choice="opt">BLOCKDEVICE</arg>
</cmdsynopsis>
<para><filename>systemd-repart.service</filename></para>
@@ -35,10 +35,10 @@
<refsect1>
<title>Description</title>
<para><command>systemd-repart</command> creates partition tables, and adds or grows partitions,
based on the configuration files described in
<citerefentry><refentrytitle>repart.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para>
<para><command>systemd-repart</command> creates partition tables, and adds or grows partitions, based on
the configuration files described in
<citerefentry><refentrytitle>repart.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>. It operates
on the block device or file image specified on the command line.</para>
<para><command>systemd-repart</command> is used when <emphasis>building</emphasis> OS images, and also
when <emphasis>deploying</emphasis> images to automatically adjust them, during boot, to the system they
@@ -53,6 +53,11 @@
<filename>systemd-repart.service</filename> service is generally run at boot in the initrd, in order to
augment the partition table of the OS before its partitions are mounted.</para>
<para>If the block device is specified as <literal>-</literal> (or as an empty string),
<command>systemd-repart</command> will not operate on any block device or image file, and instead
determine and output the minimum disk/image size for the specified partition configuration, taking all
configured size constraints into account.</para>
<para><command>systemd-repart</command> operations are mostly incremental: it grows existing partitions
or adds new ones, but does not shrink, delete, or move existing partitions. The service is intended to be
run on every boot, but when it detects that the partition table already matches the installed
@@ -495,6 +500,30 @@
<xi:include href="version-info.xml" xpointer="v253"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--defer-partitions-empty=yes</option></term>
<listitem><para>This is very similar to <option>--defer-partitions=</option> but automatically
selects all partitions for deferral that have <option>Format=empty</option> set. It may be used in
conjunction with <option>--defer-partitions=</option> or
<option>--defer-partitions-factory-reset=yes</option>, in which case all matching partitions are
deferred.</para>
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--defer-partitions-factory-reset=yes</option></term>
<listitem><para>This is very similar to <option>--defer-partitions=</option> but automatically
selects all partitions for deferral that have <option>FactoryReset=yes</option> set. It may be used
in conjunction with <option>--defer-partitions=</option> or
<option>--defer-partitions-empty=yes</option>, in which case all matching partitions are
deferred.</para>
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--sector-size=<replaceable>BYTES</replaceable></option></term>

View File

@@ -14,3 +14,11 @@ ACTION!="remove", SUBSYSTEM=="block", \
# Reset access rights to each loopback device once it gets detached.
ACTION=="change", SUBSYSTEM=="block", KERNEL=="loop*", ENV{DISK_MEDIA_CHANGE}=="1", TEST!="loop/backing_file", GROUP="disk", MODE="660"
# Provide a somewhat cleaned up field indicating the subsystem various
# 'virtual' block devices belong too, in order to avoid replicating name based
# pattern matching in every consumer
ACTION!="remove", SUBSYSTEM=="block", KERNEL=="dm-*", ENV{ID_BLOCK_SUBSYSTEM}="dm"
ACTION!="remove", SUBSYSTEM=="block", KERNEL=="loop*", ENV{ID_BLOCK_SUBSYSTEM}="loop"
ACTION!="remove", SUBSYSTEM=="block", KERNEL=="md*", ENV{ID_BLOCK_SUBSYSTEM}="md"
ACTION!="remove", SUBSYSTEM=="block", KERNEL=="zram*", ENV{ID_BLOCK_SUBSYSTEM}="zram"

View File

@@ -31,6 +31,11 @@ int json_dispatch_unhex_iovec(const char *name, sd_json_variant *variant, sd_jso
size_t sz;
int r;
if (sd_json_variant_is_null(variant)) {
iovec_done(iov);
return 0;
}
if (!sd_json_variant_is_string(variant))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
@@ -49,6 +54,11 @@ int json_dispatch_unbase64_iovec(const char *name, sd_json_variant *variant, sd_
size_t sz;
int r;
if (sd_json_variant_is_null(variant)) {
iovec_done(iov);
return 0;
}
if (!sd_json_variant_is_string(variant))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
@@ -68,6 +78,11 @@ int json_dispatch_byte_array_iovec(const char *name, sd_json_variant *variant, s
assert(variant);
if (sd_json_variant_is_null(variant)) {
iovec_done(iov);
return 0;
}
if (!sd_json_variant_is_array(variant))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
@@ -169,6 +184,11 @@ int json_dispatch_in_addr(const char *name, sd_json_variant *variant, sd_json_di
_cleanup_(iovec_done) struct iovec iov = {};
int r;
if (sd_json_variant_is_null(variant)) {
*address = (struct in_addr) {};
return 0;
}
r = json_dispatch_byte_array_iovec(name, variant, flags, &iov);
if (r < 0)
return r;

View File

@@ -5292,6 +5292,11 @@ _public_ int sd_json_dispatch_stdbool(const char *name, sd_json_variant *variant
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*b = false;
return 0;
}
if (!sd_json_variant_is_boolean(variant))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a boolean.", strna(name));
@@ -5305,6 +5310,11 @@ _public_ int sd_json_dispatch_intbool(const char *name, sd_json_variant *variant
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*b = false;
return 0;
}
if (!sd_json_variant_is_boolean(variant))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a boolean.", strna(name));
@@ -5336,6 +5346,11 @@ _public_ int sd_json_dispatch_int64(const char *name, sd_json_variant *variant,
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*i = -1;
return 0;
}
/* Also accept numbers formatted as string, to increase compatibility with less capable JSON
* implementations that cannot do 64bit integers. */
if (sd_json_variant_is_string(variant) && safe_atoi64(sd_json_variant_string(variant), i) >= 0)
@@ -5354,6 +5369,11 @@ _public_ int sd_json_dispatch_uint64(const char *name, sd_json_variant *variant,
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*u = UINT64_MAX;
return 0;
}
/* Since 64bit values (in particular unsigned ones) in JSON are problematic, let's also accept them
* formatted as strings. If this is not desired make sure to set the .type field in
* sd_json_dispatch_field to SD_JSON_UNSIGNED rather than _SD_JSON_VARIANT_TYPE_INVALID, so that
@@ -5377,6 +5397,11 @@ _public_ int sd_json_dispatch_uint32(const char *name, sd_json_variant *variant,
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*u = UINT32_MAX;
return 0;
}
r = sd_json_dispatch_uint64(name, variant, flags, &u64);
if (r < 0)
return r;
@@ -5399,6 +5424,11 @@ _public_ int sd_json_dispatch_int32(const char *name, sd_json_variant *variant,
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*i = -1;
return 0;
}
r = sd_json_dispatch_int64(name, variant, flags, &i64);
if (r < 0)
return r;
@@ -5421,6 +5451,11 @@ _public_ int sd_json_dispatch_int16(const char *name, sd_json_variant *variant,
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*i = -1;
return 0;
}
r = sd_json_dispatch_int64(name, variant, flags, &i64);
if (r < 0)
return r;
@@ -5440,6 +5475,11 @@ _public_ int sd_json_dispatch_uint16(const char *name, sd_json_variant *variant,
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*u = UINT16_MAX;
return 0;
}
r = sd_json_dispatch_uint64(name, variant, flags, &u64);
if (r < 0)
return r;
@@ -5459,6 +5499,11 @@ _public_ int sd_json_dispatch_int8(const char *name, sd_json_variant *variant, s
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*i = -1;
return 0;
}
r = sd_json_dispatch_int64(name, variant, flags, &i64);
if (r < 0)
return r;
@@ -5478,6 +5523,11 @@ _public_ int sd_json_dispatch_uint8(const char *name, sd_json_variant *variant,
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*u = UINT8_MAX;
return 0;
}
r = sd_json_dispatch_uint64(name, variant, flags, &u64);
if (r < 0)
return r;
@@ -5495,6 +5545,11 @@ _public_ int sd_json_dispatch_double(const char *name, sd_json_variant *variant,
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*d = NAN;
return 0;
}
/* Note, this will take care of parsing NaN, -Infinity, Infinity for us */
if (sd_json_variant_is_string(variant) && safe_atod(sd_json_variant_string(variant), d) >= 0)
return 0;
@@ -5514,6 +5569,11 @@ _public_ int sd_json_dispatch_string(const char *name, sd_json_variant *variant,
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
if (sd_json_variant_is_null(variant)) {
*s = mfree(*s);
return 0;
}
r = sd_json_dispatch_const_string(name, variant, flags, &n);
if (r < 0)
return r;
@@ -5616,7 +5676,8 @@ _public_ int sd_json_dispatch_variant_noref(const char *name, sd_json_variant *v
_public_ int sd_json_dispatch_uid_gid(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
uid_t *uid = userdata;
uint64_t k;
uint32_t k;
int r;
assert_return(variant, -EINVAL);
assert_return(userdata, -EINVAL);
@@ -5633,11 +5694,10 @@ _public_ int sd_json_dispatch_uid_gid(const char *name, sd_json_variant *variant
return 0;
}
if (!sd_json_variant_is_unsigned(variant))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name));
k = sd_json_variant_unsigned(variant);
if (k > UINT32_MAX || !uid_is_valid(k))
r = sd_json_dispatch_uint32(name, variant, flags, &k);
if (r < 0)
return r;
if (!uid_is_valid(k))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid UID/GID.", strna(name));
*uid = k;
@@ -5678,12 +5738,18 @@ _public_ int sd_json_dispatch_signal(const char *name, sd_json_variant *variant,
}
int k;
r = sd_json_dispatch_int(name, variant, flags, &k);
if (r < 0)
return r;
if (sd_json_variant_is_string(variant)) {
k = signal_from_string(sd_json_variant_string(variant));
if (k < 0)
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid signal.", strna(name));
} else {
r = sd_json_dispatch_int(name, variant, flags, &k);
if (r < 0)
return r;
if (!SIGNAL_VALID(k))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid signal.", strna(name));
if (!SIGNAL_VALID(k))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid signal.", strna(name));
}
*signo = k;
return 0;

View File

@@ -2421,6 +2421,15 @@ _public_ int sd_varlink_collect_full(
if (sd_json_variant_elements(collected) >= VARLINK_COLLECT_MAX)
return varlink_log_errno(v, SYNTHETIC_ERRNO(E2BIG), "Number of reply messages grew too large (%zu) while collecting.", sd_json_variant_elements(collected));
_cleanup_(sd_json_variant_unrefp) sd_json_variant *empty = NULL;
if (!p) {
r = sd_json_variant_new_array(&empty, /* array= */ NULL, /* n= */ 0);
if (r < 0)
return r;
p = empty;
}
r = sd_json_variant_append_array(&collected, p);
if (r < 0)
return varlink_log_errno(v, r, "Failed to append JSON object to array: %m");

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,8 @@
#include "blockdev-util.h"
#include "device-private.h"
#include "device-util.h"
#include "errno-util.h"
#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
@@ -16,6 +18,9 @@ void block_device_done(BlockDevice *d) {
d->node = mfree(d->node);
d->symlinks = strv_free(d->symlinks);
d->model = mfree(d->model);
d->vendor = mfree(d->vendor);
d->subsystem = mfree(d->subsystem);
}
void block_device_array_free(BlockDevice *d, size_t n_devices) {
@@ -26,6 +31,61 @@ void block_device_array_free(BlockDevice *d, size_t n_devices) {
free(d);
}
static int blockdev_get_prop(sd_device *d, const char *prop1, const char *prop2, char **ret_value) {
int r, ret = 0;
assert(d);
assert(prop1);
assert(ret_value);
FOREACH_STRING(prop, prop1, prop2) {
const char *m = NULL;
r = sd_device_get_property_value(d, prop, &m);
if (r < 0 && r != -ENOENT)
RET_GATHER(ret, log_device_debug_errno(d, r, "Failed to acquire '%s' from device, ignoring: %m", prop));
else if (!isempty(m))
return strdup_to(ret_value, m);
}
return ret < 0 ? ret : -ENOENT;
}
static int blockdev_get_subsystem(sd_device *d, char **ret_subsystem) {
int r;
assert(d);
assert(ret_subsystem);
/* We prefer the explicitly set block device subsystem property, because if it is set it's generally
* the most useful. If it's not set we'll look for the subsystem of the first parent device that
* isn't of subsystem 'block'. The former covers 'virtual' block devices such as loopback, device
* mapper, zram, while the latter covers physical block devices such as USB or NVME. */
r = blockdev_get_prop(d, "ID_BLOCK_SUBSYSTEM", /* prop2= */ NULL, ret_subsystem);
if (r >= 0)
return r;
int ret = r != -ENOENT ? r : 0;
sd_device *q = d;
for (;;) {
r = sd_device_get_parent(q, &q);
if (r < 0) {
if (r != -ENOENT)
RET_GATHER(ret, log_device_debug_errno(q, r, "Failed to get parent device, ignoring: %m"));
break;
}
const char *s = NULL;
r = sd_device_get_subsystem(q, &s);
if (r < 0)
RET_GATHER(ret, log_device_debug_errno(q, r, "Failed to get subsystem of device, ignoring: %m"));
else if (!isempty(s) && !streq(s, "block"))
return strdup_to(ret_subsystem, s);
}
return ret < 0 ? ret : -ENOENT;
}
int blockdev_list(BlockDevListFlags flags, BlockDevice **ret_devices, size_t *ret_n_devices) {
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
int r;
@@ -65,7 +125,7 @@ int blockdev_list(BlockDevListFlags flags, BlockDevice **ret_devices, size_t *re
r = sd_device_get_devname(dev, &node);
if (r < 0) {
log_warning_errno(r, "Failed to get device node of discovered block device, ignoring: %m");
log_device_warning_errno(dev, r, "Failed to get device node of discovered block device, ignoring: %m");
continue;
}
@@ -74,7 +134,7 @@ int blockdev_list(BlockDevListFlags flags, BlockDevice **ret_devices, size_t *re
r = sd_device_get_devnum(dev, &devno);
if (r < 0) {
log_warning_errno(r, "Failed to get major/minor of discovered block device, ignoring: %m");
log_device_warning_errno(dev, r, "Failed to get major/minor of discovered block device, ignoring: %m");
continue;
}
@@ -85,7 +145,7 @@ int blockdev_list(BlockDevListFlags flags, BlockDevice **ret_devices, size_t *re
if (FLAGS_SET(flags, BLOCKDEV_LIST_IGNORE_ZRAM)) {
r = device_sysname_startswith(dev, "zram");
if (r < 0) {
log_warning_errno(r, "Failed to check device name of discovered block device '%s', ignoring: %m", node);
log_device_warning_errno(dev, r, "Failed to check device name of discovered block device '%s', ignoring: %m", node);
continue;
}
if (r > 0)
@@ -95,27 +155,26 @@ int blockdev_list(BlockDevListFlags flags, BlockDevice **ret_devices, size_t *re
if (FLAGS_SET(flags, BLOCKDEV_LIST_REQUIRE_PARTITION_SCANNING)) {
r = blockdev_partscan_enabled(dev);
if (r < 0) {
log_warning_errno(r, "Unable to determine whether '%s' supports partition scanning, skipping device: %m", node);
log_device_warning_errno(dev, r, "Unable to determine whether '%s' supports partition scanning, skipping device: %m", node);
continue;
}
if (r == 0) {
log_debug("Device '%s' does not support partition scanning, skipping.", node);
log_device_debug(dev, "Device '%s' does not support partition scanning, skipping.", node);
continue;
}
}
uint64_t size = UINT64_MAX;
if (FLAGS_SET(flags, BLOCKDEV_LIST_IGNORE_EMPTY) || ret_devices) {
r = device_get_sysattr_u64(dev, "size", &size);
if (r < 0)
log_debug_errno(r, "Failed to acquire size of device '%s', ignoring: %m", node);
log_device_debug_errno(dev, r, "Failed to acquire size of device '%s', ignoring: %m", node);
else
/* the 'size' sysattr is always in multiples of 512, even on 4K sector block devices! */
assert_se(MUL_ASSIGN_SAFE(&size, 512)); /* Overflow check for coverity */
if (size == 0 && FLAGS_SET(flags, BLOCKDEV_LIST_IGNORE_EMPTY)) {
log_debug("Device '%s' has a zero size, assuming drive without a medium, skipping.", node);
log_device_debug(dev, "Device '%s' has a zero size, assuming drive without a medium, skipping.", node);
continue;
}
}
@@ -129,11 +188,18 @@ int blockdev_list(BlockDevListFlags flags, BlockDevice **ret_devices, size_t *re
strv_sort(list);
}
_cleanup_free_ char *model = NULL, *vendor = NULL, *subsystem = NULL;
if (FLAGS_SET(flags, BLOCKDEV_LIST_METADATA)) {
(void) blockdev_get_prop(dev, "ID_MODEL_FROM_DATABASE", "ID_MODEL", &model);
(void) blockdev_get_prop(dev, "ID_VENDOR_FROM_DATABASE", "ID_VENDOR", &vendor);
(void) blockdev_get_subsystem(dev, &subsystem);
}
if (ret_devices) {
uint64_t diskseq = UINT64_MAX;
r = sd_device_get_diskseq(dev, &diskseq);
if (r < 0)
log_debug_errno(r, "Failed to acquire diskseq of device '%s', ignoring: %m", node);
log_device_debug_errno(dev, r, "Failed to acquire diskseq of device '%s', ignoring: %m", node);
if (!GREEDY_REALLOC(l, n+1))
return log_oom();
@@ -147,6 +213,9 @@ int blockdev_list(BlockDevListFlags flags, BlockDevice **ret_devices, size_t *re
.symlinks = TAKE_PTR(list),
.diskseq = diskseq,
.size = size,
.model = TAKE_PTR(model),
.vendor = TAKE_PTR(vendor),
.subsystem = TAKE_PTR(subsystem),
};
} else {

View File

@@ -10,11 +10,15 @@ typedef enum BlockDevListFlags {
BLOCKDEV_LIST_REQUIRE_LUKS = 1 << 3, /* Only consider block devices with LUKS superblocks */
BLOCKDEV_LIST_IGNORE_ROOT = 1 << 4, /* Ignore the block device we are currently booted from */
BLOCKDEV_LIST_IGNORE_EMPTY = 1 << 5, /* Ignore disks of zero size (usually drives without a medium) */
BLOCKDEV_LIST_METADATA = 1 << 6, /* Fill in model, vendor, subsystem */
} BlockDevListFlags;
typedef struct BlockDevice {
char *node;
char **symlinks;
char *model;
char *vendor;
char *subsystem;
uint64_t diskseq;
uint64_t size; /* in bytes */
} BlockDevice;

View File

@@ -4,27 +4,119 @@
#include "varlink-io.systemd.Repart.h"
static SD_VARLINK_DEFINE_ENUM_TYPE(
ProgressPhase,
SD_VARLINK_DEFINE_ENUM_VALUE(loading_definitions),
SD_VARLINK_DEFINE_ENUM_VALUE(loading_table),
SD_VARLINK_DEFINE_ENUM_VALUE(opening_copy_block_sources),
SD_VARLINK_DEFINE_ENUM_VALUE(acquiring_partition_labels),
SD_VARLINK_DEFINE_ENUM_VALUE(minimizing),
SD_VARLINK_DEFINE_ENUM_VALUE(placing),
SD_VARLINK_DEFINE_ENUM_VALUE(wiping_disk),
SD_VARLINK_DEFINE_ENUM_VALUE(wiping_partition),
SD_VARLINK_DEFINE_ENUM_VALUE(copying_partition),
SD_VARLINK_DEFINE_ENUM_VALUE(formatting_partition),
SD_VARLINK_DEFINE_ENUM_VALUE(adjusting_partition),
SD_VARLINK_DEFINE_ENUM_VALUE(writing_table),
SD_VARLINK_DEFINE_ENUM_VALUE(rereading_table));
static SD_VARLINK_DEFINE_ENUM_TYPE(
EmptyMode,
SD_VARLINK_FIELD_COMMENT("Refuse to operate on disks without an existing partition table"),
SD_VARLINK_DEFINE_ENUM_VALUE(refuse),
SD_VARLINK_FIELD_COMMENT("Create a new partition table if one doesn't already exist on disk"),
SD_VARLINK_DEFINE_ENUM_VALUE(allow),
SD_VARLINK_FIELD_COMMENT("Refuse to operate on disks with an existing partition table, and create a new table if none exists"),
SD_VARLINK_DEFINE_ENUM_VALUE(require),
SD_VARLINK_FIELD_COMMENT("Always create a new partition table, potentially overwriting an existing table"),
SD_VARLINK_DEFINE_ENUM_VALUE(force));
static SD_VARLINK_DEFINE_METHOD_FULL(
Run,
SD_VARLINK_SUPPORTS_MORE,
SD_VARLINK_FIELD_COMMENT("Full path to the block device node to operate on. If omitted, dryRun must be true, in which case the minimal disk size is determined."),
SD_VARLINK_DEFINE_INPUT(node, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Decides whether to install the OS in addition to what is already on it, or if it shall be erased."),
SD_VARLINK_DEFINE_INPUT_BY_TYPE(empty, EmptyMode, 0),
SD_VARLINK_FIELD_COMMENT("If true this will ponder if the installation would fit, but does not actually write anything to disk. Must be set to false to actually make changes."),
SD_VARLINK_DEFINE_INPUT(dryRun, SD_VARLINK_BOOL, 0),
SD_VARLINK_FIELD_COMMENT("The seed value to derive partition and file system UUIDs from"),
SD_VARLINK_DEFINE_INPUT(seed, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Path to directory containing definition files."),
SD_VARLINK_DEFINE_INPUT(definitions, SD_VARLINK_STRING, SD_VARLINK_ARRAY),
SD_VARLINK_FIELD_COMMENT("If true, automatically defer creation of all partitions whose label is \"empty\"."),
SD_VARLINK_DEFINE_INPUT(deferPartitionsEmpty, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("If true, automatically defer creation of all partitions which are marked for factory reset."),
SD_VARLINK_DEFINE_INPUT(deferPartitionsFactoryReset, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("In dry-run mode returns the minimal disk size required."),
SD_VARLINK_DEFINE_OUTPUT(minimalSizeBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("In dry-run mode returns the size of the selected block device."),
SD_VARLINK_DEFINE_OUTPUT(currentSizeBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("If used with the 'more' flag, a phase identifier is sent in progress updates."),
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(phase, ProgressPhase, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("If used with the 'more' flag, an object identifier string is sent in progress updates."),
SD_VARLINK_DEFINE_OUTPUT(object, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("If used with the 'more' flag, a progress percentrage (specific to the work done for the specified phase+object is sent in progress updates."),
SD_VARLINK_DEFINE_OUTPUT(progress, SD_VARLINK_INT, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_METHOD(
ListCandidateDevices,
SD_VARLINK_FIELD_COMMENT("The device node path of the block device."),
SD_VARLINK_DEFINE_OUTPUT(node, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("Control whether to include the root disk of the currently booted OS in the list. Defaults to false, i.e. the root disk is included."),
SD_VARLINK_DEFINE_INPUT(ignoreRoot, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Control whether to include block devices with zero size in the list, i.e. typically block devices without any inserted medium. Defaults to false, i.e. empty block devices are included."),
SD_VARLINK_DEFINE_INPUT(ignoreEmpty, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The device node path of the block device."),
SD_VARLINK_DEFINE_OUTPUT(node, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("List of symlinks pointing to the device node, if any."),
SD_VARLINK_DEFINE_OUTPUT(symlinks, SD_VARLINK_STRING, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The Linux kernel disk sequence number identifying the medium."),
SD_VARLINK_DEFINE_OUTPUT(diskseq, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The size of the block device in bytes."),
SD_VARLINK_DEFINE_OUTPUT(sizeBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE));
SD_VARLINK_DEFINE_OUTPUT(sizeBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The device vendor string if known"),
SD_VARLINK_DEFINE_OUTPUT(vendor, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The device model string if known"),
SD_VARLINK_DEFINE_OUTPUT(model, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The subsystem the block device belongs to if known"),
SD_VARLINK_DEFINE_OUTPUT(subsystem, SD_VARLINK_STRING, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_ERROR(NoCandidateDevices);
static SD_VARLINK_DEFINE_ERROR(ConflictingDiskLabelPresent);
static SD_VARLINK_DEFINE_ERROR(
InsufficientFreeSpace,
SD_VARLINK_FIELD_COMMENT("Minimal size of the disk required for the installation."),
SD_VARLINK_DEFINE_FIELD(minimalSizeBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Additional free space needed on the selected disk."),
SD_VARLINK_DEFINE_FIELD(needFreeBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Size of the selected block device."),
SD_VARLINK_DEFINE_FIELD(currentSizeBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_ERROR(
DiskTooSmall,
SD_VARLINK_FIELD_COMMENT("Minimal size of the disk required for the installation."),
SD_VARLINK_DEFINE_FIELD(minimalSizeBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Actual size of the selected block device."),
SD_VARLINK_DEFINE_FIELD(currentSizeBytes, SD_VARLINK_INT, SD_VARLINK_NULLABLE));
SD_VARLINK_DEFINE_INTERFACE(
io_systemd_Repart,
"io.systemd.Repart",
SD_VARLINK_INTERFACE_COMMENT("API for declaratively re-partitioning disks using systemd-repart."),
SD_VARLINK_SYMBOL_COMMENT("Behaviors for disks that are completely empty (i.e. don't have a partition table yet)"),
&vl_type_EmptyMode,
SD_VARLINK_SYMBOL_COMMENT("Progress phase identifiers. Note that we might add more phases here, and thus identifiers. Frontends can choose to display the phase to the user in some human readable form, or not do that, but if they do it and they receive a notification for a so far unknown phase, they should just ignore it."),
&vl_type_ProgressPhase,
SD_VARLINK_SYMBOL_COMMENT("Invoke the actual repartitioning operation, either in dry-run mode or for real. If invoked with 'more' enabled will report progress, otherwise will just report completion."),
&vl_method_Run,
SD_VARLINK_SYMBOL_COMMENT("An incompatible disk label present, and not told to erase it."),
&vl_error_ConflictingDiskLabelPresent,
SD_VARLINK_SYMBOL_COMMENT("The target disk has insufficient free space to fit all requested partitions. (But the disk would fit, if emptied.)"),
&vl_error_InsufficientFreeSpace,
SD_VARLINK_SYMBOL_COMMENT("The target disk is too small to fit the installation. (Regardless if emtied or not.)"),
&vl_error_DiskTooSmall,
SD_VARLINK_SYMBOL_COMMENT("Return a list of candidate block devices, i.e. that support partition scanning and other requirements for successful operation."),
&vl_method_ListCandidateDevices,
SD_VARLINK_SYMBOL_COMMENT("Not a single candidate block device could be found."),

View File

@@ -141,6 +141,12 @@ SizeMaxBytes=64M
PaddingMinBytes=92M
EOF
systemd-repart --definitions="$defs" \
--dry-run=yes \
--seed="$seed" \
--include-partitions=home,swap \
"-"
systemd-repart --offline="$OFFLINE" \
--definitions="$defs" \
--dry-run=no \
@@ -1690,6 +1696,70 @@ testcase_varlink_list_devices() {
varlinkctl call /run/systemd/io.systemd.Repart --graceful=io.systemd.Repart.NoCandidateDevices --collect io.systemd.Repart.ListCandidateDevices '{"ignoreEmpty":true,"ignoreRoot":true}'
}
testcase_get_size() {
local defs
defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
# shellcheck disable=SC2064
trap "rm -rf '$defs'" RETURN
tee "$defs/a.conf" <<EOF
[Partition]
Type=root
SizeMinBytes=15M
EOF
tee "$defs/b.conf" <<EOF
[Partition]
Type=linux-generic
SizeMinBytes=23M
EOF
output="$(systemd-repart --definitions="$defs" - 2>&1)"
assert_in "Automatically determined minimal disk image size as 39M." "$output"
}
testcase_varlink_run() {
local defs
defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
# shellcheck disable=SC2064
trap "rm -rf '$defs' '$imgs'" RETURN
tee "$defs/a.conf" <<EOF
[Partition]
Type=root
Format=empty
EOF
tee "$defs/b.conf" <<EOF
[Partition]
Type=linux-generic
Format=empty
EOF
systemd-repart --pretty=yes \
--definitions "$defs" \
--empty=create \
--size=50M \
--seed="$seed" \
--dry-run=no \
--offline=yes \
"$imgs/disk1.img"
REPART="$(which systemd-repart)"
truncate -s 50M "$imgs/disk2.img"
varlinkctl call "$REPART" io.systemd.Repart.Run '{"definitions":["'"$defs"'"],"empty":"force","seed":"'"$seed"'","dryRun":false,"node":"'"$imgs/disk2.img"'"}'
# Compare that the version from the command line and via Varlink result in the bit exact same output
cmp "$imgs/disk1.img" "$imgs/disk2.img"
# Try once more, this time with progress info
truncate -s 50M "$imgs/disk3.img"
varlinkctl --more --collect call "$REPART" io.systemd.Repart.Run '{"definitions":["'"$defs"'"],"empty":"force","seed":"'"$seed"'","dryRun":false,"node":"'"$imgs/disk3.img"'"}'
cmp "$imgs/disk1.img" "$imgs/disk3.img"
}
OFFLINE="yes"
run_testcases