1
0
mirror of https://github.com/systemd/systemd synced 2025-10-06 00:13:24 +02:00

repart: add a very basic varlink interface (#39072)

This is split out of https://github.com/systemd/systemd/pull/38764.

It adds a very basic Varlink API to repart. Not the actual
repartitioning APIs, but simply a call to get a list of candidate
devices.

A very basic test case is added too.

Other commits from #38764 add the repartitioning API, but let's do that
in a separate PR.
This commit is contained in:
Lennart Poettering
2025-09-23 10:46:50 +02:00
committed by GitHub
14 changed files with 361 additions and 20 deletions

View File

@@ -630,7 +630,7 @@ static int parse_argv(int argc, char *argv[]) {
}
case ARG_LIST_DEVICES:
r = blockdev_list(BLOCKDEV_LIST_SHOW_SYMLINKS|BLOCKDEV_LIST_REQUIRE_LUKS);
r = blockdev_list(BLOCKDEV_LIST_SHOW_SYMLINKS|BLOCKDEV_LIST_REQUIRE_LUKS, /* ret_devices= */ NULL, /* ret_n_devices= */ NULL);
if (r < 0)
return r;

View File

@@ -21,6 +21,7 @@ static inline int device_get_sysattr_unsigned(sd_device *device, const char *sys
return device_get_sysattr_unsigned_full(device, sysattr, 0, ret_value);
}
int device_get_sysattr_u32(sd_device *device, const char *sysattr, uint32_t *ret_value);
int device_get_sysattr_u64(sd_device *device, const char *sysattr, uint64_t *ret_value);
int device_get_sysattr_bool(sd_device *device, const char *sysattr);
int device_get_devlink_priority(sd_device *device, int *ret);
int device_get_devnode_mode(sd_device *device, mode_t *ret);

View File

@@ -2652,6 +2652,25 @@ int device_get_sysattr_u32(sd_device *device, const char *sysattr, uint32_t *ret
return v > 0;
}
int device_get_sysattr_u64(sd_device *device, const char *sysattr, uint64_t *ret_value) {
const char *value;
int r;
r = sd_device_get_sysattr_value(device, sysattr, &value);
if (r < 0)
return r;
uint64_t v;
r = safe_atou64(value, &v);
if (r < 0)
return log_device_debug_errno(device, r, "Failed to parse '%s' attribute: %m", sysattr);
if (ret_value)
*ret_value = v;
/* We return "true" if the value is positive. */
return v > 0;
}
int device_get_sysattr_bool(sd_device *device, const char *sysattr) {
const char *value;
int r;

View File

@@ -9,6 +9,7 @@
#include "sd-id128.h"
#include "sd-json.h"
#include "sd-varlink.h"
#include "alloc-util.h"
#include "ask-password-api.h"
@@ -79,6 +80,8 @@
#include "tpm2-pcr.h"
#include "tpm2-util.h"
#include "utf8.h"
#include "varlink-io.systemd.Repart.h"
#include "varlink-util.h"
#include "xattr-util.h"
/* If not configured otherwise use a minimal partition size of 10M */
@@ -202,6 +205,7 @@ static char *arg_generate_fstab = NULL;
static char *arg_generate_crypttab = NULL;
static Set *arg_verity_settings = NULL;
static bool arg_relax_copy_block_security = false;
static bool arg_varlink = false;
STATIC_DESTRUCTOR_REGISTER(arg_node, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
@@ -9163,7 +9167,7 @@ static int parse_argv(
break;
case ARG_LIST_DEVICES:
r = blockdev_list(BLOCKDEV_LIST_REQUIRE_PARTITION_SCANNING|BLOCKDEV_LIST_SHOW_SYMLINKS|BLOCKDEV_LIST_IGNORE_ZRAM);
r = blockdev_list(BLOCKDEV_LIST_REQUIRE_PARTITION_SCANNING|BLOCKDEV_LIST_SHOW_SYMLINKS|BLOCKDEV_LIST_IGNORE_ZRAM, /* ret_devices= */ NULL, /* ret_n_devices= */ NULL);
if (r < 0)
return r;
@@ -9347,6 +9351,14 @@ static int parse_argv(
if (arg_append_fstab && !arg_generate_fstab)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No --generate-fstab= specified for --append-fstab=%s.", append_mode_to_string(arg_append_fstab));
r = sd_varlink_invocation(SD_VARLINK_ALLOW_ACCEPT);
if (r < 0)
return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
if (r > 0) {
arg_varlink = true;
arg_pager_flags |= PAGER_DISABLE;
}
*ret_certificate = TAKE_PTR(certificate);
*ret_private_key = TAKE_PTR(private_key);
*ret_ui = TAKE_PTR(ui);
@@ -9771,6 +9783,106 @@ static int determine_auto_size(Context *c) {
return 0;
}
static int vl_method_list_candidate_devices(
sd_varlink *link,
sd_json_variant *parameters,
sd_varlink_method_flags_t flags,
void *userdata) {
struct {
bool ignore_root;
bool ignore_empty;
} p = {};
static const sd_json_dispatch_field dispatch_table[] = {
{ "ignoreRoot", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, voffsetof(p, ignore_root), 0 },
{ "ignoreEmpty", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, voffsetof(p, ignore_empty), 0 },
{}
};
int r;
assert(link);
r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
if (r != 0)
return r;
if (!FLAGS_SET(flags, SD_VARLINK_METHOD_MORE))
return sd_varlink_error(link, SD_VARLINK_ERROR_EXPECTED_MORE, NULL);
BlockDevice *l = NULL;
size_t n = 0;
CLEANUP_ARRAY(l, n, block_device_array_free);
r = blockdev_list(
BLOCKDEV_LIST_SHOW_SYMLINKS|
BLOCKDEV_LIST_REQUIRE_PARTITION_SCANNING|
BLOCKDEV_LIST_IGNORE_ZRAM|
(p.ignore_empty ? BLOCKDEV_LIST_IGNORE_EMPTY : 0)|
(p.ignore_root ? BLOCKDEV_LIST_IGNORE_ROOT : 0),
&l,
&n);
if (r < 0)
return r;
if (n == 0)
return sd_varlink_error(link, "io.systemd.Repart.NoCandidateDevices", NULL);
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
FOREACH_ARRAY(d, l, n) {
if (v) {
r = sd_varlink_notify(link, v);
if (r < 0)
return r;
v = sd_json_variant_unref(v);
}
r = sd_json_buildo(
&v,
SD_JSON_BUILD_PAIR_STRING("node", d->node),
JSON_BUILD_PAIR_STRV_NON_EMPTY("symlinks", d->symlinks),
SD_JSON_BUILD_PAIR_CONDITION(d->diskseq != UINT64_MAX, "diskseq", SD_JSON_BUILD_INTEGER(d->diskseq)),
SD_JSON_BUILD_PAIR_CONDITION(d->size != UINT64_MAX, "sizeBytes", SD_JSON_BUILD_INTEGER(d->size)));
if (r < 0)
return r;
}
assert(v);
return sd_varlink_reply(link, v);
}
static int vl_server(void) {
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
int r;
/* Invocation as Varlink service */
r = varlink_server_new(
&varlink_server,
SD_VARLINK_SERVER_ROOT_ONLY,
/* userdata= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to allocate Varlink server: %m");
r = sd_varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_Repart);
if (r < 0)
return log_error_errno(r, "Failed to add Varlink interface: %m");
r = sd_varlink_server_bind_method_many(
varlink_server,
"io.systemd.Repart.ListCandidateDevices", vl_method_list_candidate_devices);
if (r < 0)
return log_error_errno(r, "Failed to bind Varlink methods: %m");
r = sd_varlink_server_loop_auto(varlink_server);
if (r < 0)
return log_error_errno(r, "Failed to run Varlink event loop: %m");
return 0;
}
static int run(int argc, char *argv[]) {
_cleanup_(X509_freep) X509 *certificate = NULL;
_cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL;
@@ -9787,6 +9899,13 @@ static int run(int argc, char *argv[]) {
if (r <= 0)
return r;
#if HAVE_LIBCRYPTSETUP
cryptsetup_enable_logging(NULL);
#endif
if (arg_varlink)
return vl_server();
r = parse_proc_cmdline_factory_reset();
if (r < 0)
return r;
@@ -9795,10 +9914,6 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
#if HAVE_LIBCRYPTSETUP
cryptsetup_enable_logging(NULL);
#endif
if (arg_image) {
assert(!arg_root);

View File

@@ -2,17 +2,51 @@
#include "sd-device.h"
#include "alloc-util.h"
#include "ansi-color.h"
#include "blockdev-list.h"
#include "blockdev-util.h"
#include "device-private.h"
#include "device-util.h"
#include "strv.h"
#include "terminal-util.h"
int blockdev_list(BlockDevListFlags flags) {
void block_device_done(BlockDevice *d) {
assert(d);
d->node = mfree(d->node);
d->symlinks = strv_free(d->symlinks);
}
void block_device_array_free(BlockDevice *d, size_t n_devices) {
FOREACH_ARRAY(i, d, n_devices)
block_device_done(d);
free(d);
}
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;
assert(!!ret_devices == !!ret_n_devices);
/* If ret_devices/ret_n_devices are passed, returns a list of matching block devices, otherwise
* prints the list to stdout */
BlockDevice *l = NULL;
size_t n = 0;
CLEANUP_ARRAY(l, n, block_device_array_free);
dev_t root_devno = 0;
if (FLAGS_SET(flags, BLOCKDEV_LIST_IGNORE_ROOT))
if (blockdev_get_root(LOG_DEBUG, &root_devno) > 0) {
r = block_get_whole_disk(root_devno, &root_devno);
if (r < 0)
log_debug_errno(r, "Failed to get whole block device of root device: %m");
}
if (sd_device_enumerator_new(&e) < 0)
return log_oom();
@@ -35,6 +69,19 @@ int blockdev_list(BlockDevListFlags flags) {
continue;
}
if (FLAGS_SET(flags, BLOCKDEV_LIST_IGNORE_ROOT) && root_devno != 0) {
dev_t devno;
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");
continue;
}
if (devno == root_devno)
continue;
}
if (FLAGS_SET(flags, BLOCKDEV_LIST_IGNORE_ZRAM)) {
r = device_sysname_startswith(dev, "zram");
if (r < 0) {
@@ -57,21 +104,63 @@ int blockdev_list(BlockDevListFlags flags) {
}
}
printf("%s\n", node);
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);
else
size *= 512; /* the 'size' sysattr is always in multiples of 512, even on 4K sector block devices! */
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);
continue;
}
}
_cleanup_strv_free_ char **list = NULL;
if (FLAGS_SET(flags, BLOCKDEV_LIST_SHOW_SYMLINKS)) {
_cleanup_strv_free_ char **list = NULL;
FOREACH_DEVICE_DEVLINK(dev, l)
if (strv_extend(&list, l) < 0)
FOREACH_DEVICE_DEVLINK(dev, sl)
if (strv_extend(&list, sl) < 0)
return log_oom();
strv_sort(list);
}
STRV_FOREACH(i, list)
printf("%s%s%s%s\n", on_tty() ? " " : "", ansi_grey(), *i, ansi_normal());
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);
if (!GREEDY_REALLOC(l, n+1))
return log_oom();
_cleanup_free_ char *m = strdup(node);
if (!m)
return log_oom();
l[n++] = (BlockDevice) {
.node = TAKE_PTR(m),
.symlinks = TAKE_PTR(list),
.diskseq = diskseq,
.size = size,
};
} else {
printf("%s\n", node);
if (FLAGS_SET(flags, BLOCKDEV_LIST_SHOW_SYMLINKS))
STRV_FOREACH(i, list)
printf("%s%s%s%s\n", on_tty() ? " " : "", ansi_grey(), *i, ansi_normal());
}
}
if (ret_devices)
*ret_devices = TAKE_PTR(l);
if (ret_n_devices)
*ret_n_devices = n;
return 0;
}

View File

@@ -4,10 +4,27 @@
#include "forward.h"
typedef enum BlockDevListFlags {
BLOCKDEV_LIST_SHOW_SYMLINKS = 1 << 0,
BLOCKDEV_LIST_REQUIRE_PARTITION_SCANNING = 1 << 1,
BLOCKDEV_LIST_IGNORE_ZRAM = 1 << 2,
BLOCKDEV_LIST_REQUIRE_LUKS = 1 << 3,
BLOCKDEV_LIST_SHOW_SYMLINKS = 1 << 0, /* Pick up symlinks to block devices too */
BLOCKDEV_LIST_REQUIRE_PARTITION_SCANNING = 1 << 1, /* Only consider block devices with partition scanning */
BLOCKDEV_LIST_IGNORE_ZRAM = 1 << 2, /* Ignore ZRAM */
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) */
} BlockDevListFlags;
int blockdev_list(BlockDevListFlags flags);
typedef struct BlockDevice {
char *node;
char **symlinks;
uint64_t diskseq;
uint64_t size; /* in bytes */
} BlockDevice;
#define BLOCK_DEVICE_NULL (BlockDevice) { \
.diskseq = UINT64_MAX, \
.size = UINT64_MAX, \
}
void block_device_done(BlockDevice *d);
void block_device_array_free(BlockDevice *d, size_t n_devices);
int blockdev_list(BlockDevListFlags flags, BlockDevice **ret_devices, size_t *ret_n_devices);

View File

@@ -204,6 +204,7 @@ shared_sources = files(
'varlink-io.systemd.Network.c',
'varlink-io.systemd.PCRExtend.c',
'varlink-io.systemd.PCRLock.c',
'varlink-io.systemd.Repart.c',
'varlink-io.systemd.Resolve.c',
'varlink-io.systemd.Resolve.Monitor.c',
'varlink-io.systemd.Udev.c',

View File

@@ -0,0 +1,31 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "sd-varlink-idl.h"
#include "varlink-io.systemd.Repart.h"
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("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));
static SD_VARLINK_DEFINE_ERROR(NoCandidateDevices);
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("Return a list of candidate block devices, i.e. that support partition scanning and other requirements for successful operation."),
&vl_method_ListCandidateDevices,
SD_VARLINK_SYMBOL_COMMENT("Not a single candidate block device could be found."),
&vl_error_NoCandidateDevices);

View File

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

View File

@@ -115,7 +115,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_LIST_DEVICES:
r = blockdev_list(BLOCKDEV_LIST_SHOW_SYMLINKS|BLOCKDEV_LIST_IGNORE_ZRAM);
r = blockdev_list(BLOCKDEV_LIST_SHOW_SYMLINKS|BLOCKDEV_LIST_IGNORE_ZRAM, /* ret_devices= */ NULL, /* ret_n_devices= */ NULL);
if (r < 0)
return r;

View File

@@ -1679,6 +1679,17 @@ EOF
grep -q 'UUID=[0-9a-f-]* /home btrfs discard,rw,nodev,suid,exec,subvol=@home,zstd:1,noatime,lazytime 0 1' "$root"/etc/fstab
}
testcase_varlink_list_devices() {
REPART="$(which systemd-repart)"
varlinkctl introspect "$REPART"
varlinkctl call "$REPART" --graceful=io.systemd.Repart.NoCandidateDevices --collect io.systemd.Repart.ListCandidateDevices '{}'
varlinkctl call "$REPART" --graceful=io.systemd.Repart.NoCandidateDevices --collect io.systemd.Repart.ListCandidateDevices '{"ignoreRoot":true}'
varlinkctl call "$REPART" --graceful=io.systemd.Repart.NoCandidateDevices --collect io.systemd.Repart.ListCandidateDevices '{"ignoreEmpty":true}'
varlinkctl call "$REPART" --graceful=io.systemd.Repart.NoCandidateDevices --collect io.systemd.Repart.ListCandidateDevices '{"ignoreEmpty":true,"ignoreRoot":true}'
varlinkctl call /run/systemd/io.systemd.Repart --graceful=io.systemd.Repart.NoCandidateDevices --collect io.systemd.Repart.ListCandidateDevices '{"ignoreEmpty":true,"ignoreRoot":true}'
}
OFFLINE="yes"
run_testcases

View File

@@ -651,6 +651,15 @@ units = [
'conditions' : ['ENABLE_REPART'],
'symlinks' : ['sysinit.target.wants/', 'initrd-root-fs.target.wants/'],
},
{
'file' : 'systemd-repart.socket',
'conditions' : ['ENABLE_REPART'],
'symlinks' : ['sockets.target.wants/'],
},
{
'file' : 'systemd-repart@.service',
'conditions' : ['ENABLE_REPART'],
},
{
'file' : 'systemd-resolved.service.in',
'conditions' : ['ENABLE_RESOLVE'],

View File

@@ -0,0 +1,22 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Disk Repartitioning Service Socket
Documentation=man:systemd-repart(8)
DefaultDependencies=no
Before=sockets.target
Conflicts=shutdown.target
Before=shutdown.target
[Socket]
ListenStream=/run/systemd/io.systemd.Repart
FileDescriptorName=varlink
SocketMode=0600
Accept=yes

View File

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