From cb06a804823a762aedc2b6de708aec934c497623 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 28 Aug 2025 10:52:56 +0200 Subject: [PATCH 1/7] sd-device: add device_get_sysattr_u64() helper --- src/libsystemd/sd-device/device-private.h | 1 + src/libsystemd/sd-device/sd-device.c | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/libsystemd/sd-device/device-private.h b/src/libsystemd/sd-device/device-private.h index d80e795154..5e75c26619 100644 --- a/src/libsystemd/sd-device/device-private.h +++ b/src/libsystemd/sd-device/device-private.h @@ -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); diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index 9fef17a0dc..3edebdda66 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -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; From 9f6b2745eaa15be80568fde2a44d0a10ed6eb2a1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 28 Aug 2025 10:40:42 +0200 Subject: [PATCH 2/7] blockdev-list: optionally return finds as list instead of writing it to stdout --- src/cryptenroll/cryptenroll.c | 2 +- src/repart/repart.c | 2 +- src/shared/blockdev-list.c | 69 +++++++++++++++++++++++++++++++---- src/shared/blockdev-list.h | 16 +++++++- src/storagetm/storagetm.c | 2 +- 5 files changed, 79 insertions(+), 12 deletions(-) diff --git a/src/cryptenroll/cryptenroll.c b/src/cryptenroll/cryptenroll.c index 2f20af7e52..5a0d972242 100644 --- a/src/cryptenroll/cryptenroll.c +++ b/src/cryptenroll/cryptenroll.c @@ -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; diff --git a/src/repart/repart.c b/src/repart/repart.c index 5e312ce086..74c348d030 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -9163,7 +9163,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; diff --git a/src/shared/blockdev-list.c b/src/shared/blockdev-list.c index 0e7cd99fad..d61b86c0c0 100644 --- a/src/shared/blockdev-list.c +++ b/src/shared/blockdev-list.c @@ -2,6 +2,7 @@ #include "sd-device.h" +#include "alloc-util.h" #include "ansi-color.h" #include "blockdev-list.h" #include "blockdev-util.h" @@ -9,10 +10,34 @@ #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); + if (sd_device_enumerator_new(&e) < 0) return log_oom(); @@ -57,21 +82,49 @@ int blockdev_list(BlockDevListFlags flags) { } } - printf("%s\n", node); + _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, + }; + + } 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; } diff --git a/src/shared/blockdev-list.h b/src/shared/blockdev-list.h index f569cf22e3..c77952993e 100644 --- a/src/shared/blockdev-list.h +++ b/src/shared/blockdev-list.h @@ -10,4 +10,18 @@ typedef enum BlockDevListFlags { BLOCKDEV_LIST_REQUIRE_LUKS = 1 << 3, } BlockDevListFlags; -int blockdev_list(BlockDevListFlags flags); +typedef struct BlockDevice { + char *node; + char **symlinks; + uint64_t diskseq; +} 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); diff --git a/src/storagetm/storagetm.c b/src/storagetm/storagetm.c index 0a4f8541e9..972b892ca1 100644 --- a/src/storagetm/storagetm.c +++ b/src/storagetm/storagetm.c @@ -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; From e863e2dbb57d2d54e8ef2d6dd9a057b6a5e2977b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 28 Aug 2025 10:56:06 +0200 Subject: [PATCH 3/7] blockdev-list: also pick up block device size --- src/shared/blockdev-list.c | 10 +++++++++- src/shared/blockdev-list.h | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/shared/blockdev-list.c b/src/shared/blockdev-list.c index d61b86c0c0..3c54219393 100644 --- a/src/shared/blockdev-list.c +++ b/src/shared/blockdev-list.c @@ -6,6 +6,7 @@ #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" @@ -93,12 +94,18 @@ int blockdev_list(BlockDevListFlags flags, BlockDevice **ret_devices, size_t *re } if (ret_devices) { - uint64_t diskseq = UINT64_MAX; + uint64_t diskseq = UINT64_MAX, size = 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); + 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 (!GREEDY_REALLOC(l, n+1)) return log_oom(); @@ -110,6 +117,7 @@ int blockdev_list(BlockDevListFlags flags, BlockDevice **ret_devices, size_t *re .node = TAKE_PTR(m), .symlinks = TAKE_PTR(list), .diskseq = diskseq, + .size = size, }; } else { diff --git a/src/shared/blockdev-list.h b/src/shared/blockdev-list.h index c77952993e..63e9fad10d 100644 --- a/src/shared/blockdev-list.h +++ b/src/shared/blockdev-list.h @@ -14,6 +14,7 @@ typedef struct BlockDevice { char *node; char **symlinks; uint64_t diskseq; + uint64_t size; /* in bytes */ } BlockDevice; #define BLOCK_DEVICE_NULL (BlockDevice) { \ From 19f2baccced128ea9a3f823e6007aab946b39e39 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 28 Aug 2025 11:19:41 +0200 Subject: [PATCH 4/7] repart: add basic Varlink support, for now only with a ListCandidateDevices() call --- src/repart/repart.c | 105 ++++++++++++++++++++++++- src/shared/meson.build | 1 + src/shared/varlink-io.systemd.Repart.c | 27 +++++++ src/shared/varlink-io.systemd.Repart.h | 6 ++ units/meson.build | 9 +++ units/systemd-repart.socket | 22 ++++++ units/systemd-repart@.service | 20 +++++ 7 files changed, 186 insertions(+), 4 deletions(-) create mode 100644 src/shared/varlink-io.systemd.Repart.c create mode 100644 src/shared/varlink-io.systemd.Repart.h create mode 100644 units/systemd-repart.socket create mode 100644 units/systemd-repart@.service diff --git a/src/repart/repart.c b/src/repart/repart.c index 74c348d030..0c7b77f845 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -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); @@ -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,88 @@ 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) { + + int r; + + assert(link); + + r = sd_varlink_dispatch(link, parameters, /* dispatch_table= */ NULL, /* userdata= */ NULL); + 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, &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 +9881,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 +9896,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); diff --git a/src/shared/meson.build b/src/shared/meson.build index 790b1f2825..4b84dd9cea 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -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', diff --git a/src/shared/varlink-io.systemd.Repart.c b/src/shared/varlink-io.systemd.Repart.c new file mode 100644 index 0000000000..4f531fe39c --- /dev/null +++ b/src/shared/varlink-io.systemd.Repart.c @@ -0,0 +1,27 @@ +/* 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("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); diff --git a/src/shared/varlink-io.systemd.Repart.h b/src/shared/varlink-io.systemd.Repart.h new file mode 100644 index 0000000000..7c7cb7dd79 --- /dev/null +++ b/src/shared/varlink-io.systemd.Repart.h @@ -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; diff --git a/units/meson.build b/units/meson.build index 4f47a3b2bd..c5b99e4e04 100644 --- a/units/meson.build +++ b/units/meson.build @@ -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'], diff --git a/units/systemd-repart.socket b/units/systemd-repart.socket new file mode 100644 index 0000000000..0c77165e1c --- /dev/null +++ b/units/systemd-repart.socket @@ -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 diff --git a/units/systemd-repart@.service b/units/systemd-repart@.service new file mode 100644 index 0000000000..f8061edd0a --- /dev/null +++ b/units/systemd-repart@.service @@ -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 From ed90a0cdc9e407c024135096c03f8e8228cfafac Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 28 Aug 2025 11:18:16 +0200 Subject: [PATCH 5/7] blockdev-list,repart: optionally, filter list of candidate block device and remove OS root disk --- src/repart/repart.c | 17 +++++++++++++++-- src/shared/blockdev-list.c | 21 +++++++++++++++++++++ src/shared/blockdev-list.h | 9 +++++---- src/shared/varlink-io.systemd.Repart.c | 2 ++ 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/repart/repart.c b/src/repart/repart.c index 0c7b77f845..71d73f0783 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -9789,11 +9789,18 @@ static int vl_method_list_candidate_devices( sd_varlink_method_flags_t flags, void *userdata) { + bool ignore_root = false; + + static const sd_json_dispatch_field dispatch_table[] = { + { "ignoreRoot", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, 0, 0 }, + {} + }; + int r; assert(link); - r = sd_varlink_dispatch(link, parameters, /* dispatch_table= */ NULL, /* userdata= */ NULL); + r = sd_varlink_dispatch(link, parameters, dispatch_table, &ignore_root); if (r != 0) return r; @@ -9804,7 +9811,13 @@ static int vl_method_list_candidate_devices( 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, &l, &n); + r = blockdev_list( + BLOCKDEV_LIST_SHOW_SYMLINKS| + BLOCKDEV_LIST_REQUIRE_PARTITION_SCANNING| + BLOCKDEV_LIST_IGNORE_ZRAM| + (ignore_root ? BLOCKDEV_LIST_IGNORE_ROOT : 0), + &l, + &n); if (r < 0) return r; diff --git a/src/shared/blockdev-list.c b/src/shared/blockdev-list.c index 3c54219393..7e6b5b7e47 100644 --- a/src/shared/blockdev-list.c +++ b/src/shared/blockdev-list.c @@ -39,6 +39,14 @@ int blockdev_list(BlockDevListFlags flags, BlockDevice **ret_devices, size_t *re 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(); @@ -61,6 +69,19 @@ int blockdev_list(BlockDevListFlags flags, BlockDevice **ret_devices, size_t *re 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) { diff --git a/src/shared/blockdev-list.h b/src/shared/blockdev-list.h index 63e9fad10d..525c34f49b 100644 --- a/src/shared/blockdev-list.h +++ b/src/shared/blockdev-list.h @@ -4,10 +4,11 @@ #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 */ } BlockDevListFlags; typedef struct BlockDevice { diff --git a/src/shared/varlink-io.systemd.Repart.c b/src/shared/varlink-io.systemd.Repart.c index 4f531fe39c..f1ea620990 100644 --- a/src/shared/varlink-io.systemd.Repart.c +++ b/src/shared/varlink-io.systemd.Repart.c @@ -8,6 +8,8 @@ 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("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."), From ba793df4b93a4d5f658393aaabfcad355ecda20a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 5 Sep 2025 14:23:12 +0200 Subject: [PATCH 6/7] blockdev-list,repart: optionally hide zero-size block devices Block devices with removable media (e.g. SD card readers) indicate a missing medium with a zero size. Optionally ignore such block devices that carry no medium currently. --- src/repart/repart.c | 13 +++++++++---- src/shared/blockdev-list.c | 23 +++++++++++++++-------- src/shared/blockdev-list.h | 1 + src/shared/varlink-io.systemd.Repart.c | 2 ++ 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/repart/repart.c b/src/repart/repart.c index 71d73f0783..a27a75c993 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -9789,10 +9789,14 @@ static int vl_method_list_candidate_devices( sd_varlink_method_flags_t flags, void *userdata) { - bool ignore_root = false; + 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, 0, 0 }, + { "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 }, {} }; @@ -9800,7 +9804,7 @@ static int vl_method_list_candidate_devices( assert(link); - r = sd_varlink_dispatch(link, parameters, dispatch_table, &ignore_root); + r = sd_varlink_dispatch(link, parameters, dispatch_table, &p); if (r != 0) return r; @@ -9815,7 +9819,8 @@ static int vl_method_list_candidate_devices( BLOCKDEV_LIST_SHOW_SYMLINKS| BLOCKDEV_LIST_REQUIRE_PARTITION_SCANNING| BLOCKDEV_LIST_IGNORE_ZRAM| - (ignore_root ? BLOCKDEV_LIST_IGNORE_ROOT : 0), + (p.ignore_empty ? BLOCKDEV_LIST_IGNORE_EMPTY : 0)| + (p.ignore_root ? BLOCKDEV_LIST_IGNORE_ROOT : 0), &l, &n); if (r < 0) diff --git a/src/shared/blockdev-list.c b/src/shared/blockdev-list.c index 7e6b5b7e47..16a0e2bb25 100644 --- a/src/shared/blockdev-list.c +++ b/src/shared/blockdev-list.c @@ -104,6 +104,20 @@ int blockdev_list(BlockDevListFlags flags, BlockDevice **ret_devices, size_t *re } } + 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)) { @@ -115,18 +129,11 @@ int blockdev_list(BlockDevListFlags flags, BlockDevice **ret_devices, size_t *re } if (ret_devices) { - uint64_t diskseq = UINT64_MAX, size = UINT64_MAX; - + 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); - 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 (!GREEDY_REALLOC(l, n+1)) return log_oom(); diff --git a/src/shared/blockdev-list.h b/src/shared/blockdev-list.h index 525c34f49b..d03daffc3d 100644 --- a/src/shared/blockdev-list.h +++ b/src/shared/blockdev-list.h @@ -9,6 +9,7 @@ typedef enum BlockDevListFlags { 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; typedef struct BlockDevice { diff --git a/src/shared/varlink-io.systemd.Repart.c b/src/shared/varlink-io.systemd.Repart.c index f1ea620990..ba02b7da53 100644 --- a/src/shared/varlink-io.systemd.Repart.c +++ b/src/shared/varlink-io.systemd.Repart.c @@ -10,6 +10,8 @@ static SD_VARLINK_DEFINE_METHOD( 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."), From 347a3c925cf7e7834a8fa145d9191a29656a712a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 22 Sep 2025 12:19:42 +0200 Subject: [PATCH 7/7] test: add simple testcase for io.systemd.Repart.ListCandidateDevices --- test/units/TEST-58-REPART.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/units/TEST-58-REPART.sh b/test/units/TEST-58-REPART.sh index 19f9ca31c9..014c00b3b6 100755 --- a/test/units/TEST-58-REPART.sh +++ b/test/units/TEST-58-REPART.sh @@ -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