1
0
mirror of https://github.com/systemd/systemd synced 2025-11-22 10:14:45 +01:00

Compare commits

...

10 Commits

Author SHA1 Message Date
Igor Opaniuk
64376936c7 boot: add support for overriding key enrollement timeout
Currently, a 15-second timeout is hardcoded for the key enrollment
process while waiting for user confirmation. Make this value configurable
to allow the option of disabling user input, such as during automatic key
provisioning at the factory.

Signed-off-by: Igor Opaniuk <igor.opaniuk@foundries.io>
2025-09-23 12:13:10 +02:00
Joshua Krusell
e216876fc6 Fix sd_bus_can_send signature in manpage 2025-09-23 11:04:42 +01:00
Lennart Poettering
4d74637310
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.
2025-09-23 10:46:50 +02:00
Lennart Poettering
347a3c925c test: add simple testcase for io.systemd.Repart.ListCandidateDevices 2025-09-23 09:25:11 +02:00
Lennart Poettering
ba793df4b9 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.
2025-09-23 09:25:11 +02:00
Lennart Poettering
ed90a0cdc9 blockdev-list,repart: optionally, filter list of candidate block device and remove OS root disk 2025-09-23 09:23:55 +02:00
Lennart Poettering
19f2baccce repart: add basic Varlink support, for now only with a ListCandidateDevices() call 2025-09-23 09:22:04 +02:00
Lennart Poettering
e863e2dbb5 blockdev-list: also pick up block device size 2025-09-23 09:18:21 +02:00
Lennart Poettering
9f6b2745ea blockdev-list: optionally return finds as list instead of writing it to stdout 2025-09-23 09:13:13 +02:00
Lennart Poettering
cb06a80482 sd-device: add device_get_sysattr_u64() helper 2025-09-23 09:13:13 +02:00
19 changed files with 422 additions and 44 deletions

View File

@ -421,6 +421,17 @@ sbvarsign --attr "${attr}" --key KEK.key --cert KEK.pem --output db.auth db db.e
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term>secure-boot-enroll-timeout-sec</term>
<listitem>
<para>How long the warning should be shown in seconds before the key enrollment process starts.</para>
<para>If set to <literal>hidden</literal> or <literal>0</literal>, no warning is shown and the keys
will be enrolled immediately. The default timeout (if not set explicitly) is 15 seconds.</para>
<para>This option is only relevant if <literal>secure-boot-enroll</literal> is enabled.</para>
<xi:include href="version-info.xml" xpointer="v259"/>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term>reboot-for-bitlocker</term> <term>reboot-for-bitlocker</term>

View File

@ -27,7 +27,7 @@
<funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo> <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
<funcprototype> <funcprototype>
<funcdef>void <function>sd_bus_can_send</function></funcdef> <funcdef>int <function>sd_bus_can_send</function></funcdef>
<paramdef>sd_bus *<parameter>bus</parameter></paramdef> <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
<paramdef>char <parameter>type</parameter></paramdef> <paramdef>char <parameter>type</parameter></paramdef>
</funcprototype> </funcprototype>

View File

@ -144,6 +144,7 @@ typedef struct {
RebootOnError reboot_on_error; RebootOnError reboot_on_error;
secure_boot_enroll secure_boot_enroll; secure_boot_enroll secure_boot_enroll;
secure_boot_enroll_action secure_boot_enroll_action; secure_boot_enroll_action secure_boot_enroll_action;
uint64_t secure_boot_enroll_timeout_sec;
bool force_menu; bool force_menu;
bool use_saved_entry; bool use_saved_entry;
bool use_saved_entry_efivar; bool use_saved_entry_efivar;
@ -341,7 +342,8 @@ static void print_status(Config *config, char16_t *loaded_image_path) {
printf(" reboot-for-bitlocker: %ls\n", yes_no(config->reboot_for_bitlocker)); printf(" reboot-for-bitlocker: %ls\n", yes_no(config->reboot_for_bitlocker));
printf(" reboot-on-error: %s\n", reboot_on_error_to_string(config->reboot_on_error)); printf(" reboot-on-error: %s\n", reboot_on_error_to_string(config->reboot_on_error));
printf(" secure-boot-enroll: %s\n", secure_boot_enroll_to_string(config->secure_boot_enroll)); printf(" secure-boot-enroll: %s\n", secure_boot_enroll_to_string(config->secure_boot_enroll));
printf("secure-boot-enroll-action: %s\n", secure_boot_enroll_action_to_string(config->secure_boot_enroll_action)); printf(" secure-boot-enroll-action: %s\n", secure_boot_enroll_action_to_string(config->secure_boot_enroll_action));
printf("secure-boot-enroll-timeout-sec: %" PRIu64 "\n", config->secure_boot_enroll_timeout_sec);
switch (config->console_mode) { switch (config->console_mode) {
case CONSOLE_MODE_AUTO: case CONSOLE_MODE_AUTO:
@ -1097,6 +1099,18 @@ static void config_defaults_load_from_file(Config *config, char *content) {
else else
log_error("Error parsing 'secure-boot-enroll-action' config option, ignoring: %s", log_error("Error parsing 'secure-boot-enroll-action' config option, ignoring: %s",
value); value);
} else if (streq8(key, "secure-boot-enroll-timeout-sec")) {
if (streq8(value, "hidden"))
config->secure_boot_enroll_timeout_sec = ENROLL_TIMEOUT_HIDDEN;
else {
uint64_t u;
if (!parse_number8(value, &u, NULL) || u > ENROLL_TIMEOUT_TYPE_MAX) {
log_error("Error parsing 'secure-boot-enroll-timeout-sec' config option, ignoring: %s",
value);
continue;
}
config->secure_boot_enroll_timeout_sec = u;
}
} else if (streq8(key, "console-mode")) { } else if (streq8(key, "console-mode")) {
if (streq8(value, "auto")) if (streq8(value, "auto"))
config->console_mode = CONSOLE_MODE_AUTO; config->console_mode = CONSOLE_MODE_AUTO;
@ -1463,6 +1477,7 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
.reboot_on_error = REBOOT_AUTO, .reboot_on_error = REBOOT_AUTO,
.secure_boot_enroll = ENROLL_IF_SAFE, .secure_boot_enroll = ENROLL_IF_SAFE,
.secure_boot_enroll_action = ENROLL_ACTION_REBOOT, .secure_boot_enroll_action = ENROLL_ACTION_REBOOT,
.secure_boot_enroll_timeout_sec = ENROLL_TIMEOUT_DEFAULT,
.idx_default_efivar = IDX_INVALID, .idx_default_efivar = IDX_INVALID,
.console_mode = CONSOLE_MODE_KEEP, .console_mode = CONSOLE_MODE_KEEP,
.console_mode_efivar = CONSOLE_MODE_KEEP, .console_mode_efivar = CONSOLE_MODE_KEEP,
@ -2781,7 +2796,8 @@ static void save_selected_entry(const Config *config, const BootEntry *entry) {
static EFI_STATUS call_secure_boot_enroll(const BootEntry *entry, EFI_FILE *root_dir, EFI_HANDLE parent_image) { static EFI_STATUS call_secure_boot_enroll(const BootEntry *entry, EFI_FILE *root_dir, EFI_HANDLE parent_image) {
assert(entry); assert(entry);
return secure_boot_enroll_at(root_dir, entry->directory, /* force= */ true, /* action= */ ENROLL_ACTION_REBOOT); return secure_boot_enroll_at(root_dir, entry->directory, /* force= */ true, /* action= */ ENROLL_ACTION_REBOOT,
ENROLL_TIMEOUT_DEFAULT);
} }
static EFI_STATUS secure_boot_discover_keys(Config *config, EFI_FILE *root_dir) { static EFI_STATUS secure_boot_discover_keys(Config *config, EFI_FILE *root_dir) {
@ -2832,7 +2848,8 @@ static EFI_STATUS secure_boot_discover_keys(Config *config, EFI_FILE *root_dir)
strcaseeq16(dirent->FileName, u"auto")) strcaseeq16(dirent->FileName, u"auto"))
/* If we auto enroll successfully this call does not return. /* If we auto enroll successfully this call does not return.
* If it fails we still want to add other potential entries to the menu. */ * If it fails we still want to add other potential entries to the menu. */
secure_boot_enroll_at(root_dir, entry->directory, config->secure_boot_enroll == ENROLL_FORCE, config->secure_boot_enroll_action); secure_boot_enroll_at(root_dir, entry->directory, config->secure_boot_enroll == ENROLL_FORCE,
config->secure_boot_enroll_action, config->secure_boot_enroll_timeout_sec);
} }
return EFI_SUCCESS; return EFI_SUCCESS;

View File

@ -3,6 +3,7 @@
#include "console.h" #include "console.h"
#include "efi-efivars.h" #include "efi-efivars.h"
#include "efi-log.h" #include "efi-log.h"
#include "efi-string.h"
#include "efi-string-table.h" #include "efi-string-table.h"
#include "proto/security-arch.h" #include "proto/security-arch.h"
#include "secure-boot.h" #include "secure-boot.h"
@ -70,7 +71,8 @@ static EFI_STATUS set_custom_mode(bool enable) {
attr, sizeof(mode), &mode); attr, sizeof(mode), &mode);
} }
EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path, bool force, secure_boot_enroll_action action) { EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path, bool force,
secure_boot_enroll_action action, uint64_t timeout_sec) {
assert(root_dir); assert(root_dir);
assert(path); assert(path);
@ -88,12 +90,11 @@ EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path, bool
printf("Enrolling secure boot keys from directory: %ls\n", path); printf("Enrolling secure boot keys from directory: %ls\n", path);
if (!is_safe) { if (!is_safe && timeout_sec != ENROLL_TIMEOUT_HIDDEN) {
printf("Warning: Enrolling custom Secure Boot keys might soft-brick your machine!\n"); printf("Warning: Enrolling custom Secure Boot keys might soft-brick your machine!\n");
unsigned timeout_sec = 15;
for (;;) { for (;;) {
printf("\rEnrolling in %2u s, press any key to abort.", timeout_sec); printf("\rEnrolling in %" PRIu64 " s, press any key to abort.", timeout_sec);
err = console_key_read(/* ret_key= */ NULL, /* timeout_usec= */ 1000 * 1000); err = console_key_read(/* ret_key= */ NULL, /* timeout_usec= */ 1000 * 1000);
if (err == EFI_NOT_READY) if (err == EFI_NOT_READY)

View File

@ -18,10 +18,18 @@ typedef enum {
_SECURE_BOOT_ENROLL_ACTION_MAX, _SECURE_BOOT_ENROLL_ACTION_MAX,
} secure_boot_enroll_action; } secure_boot_enroll_action;
enum {
ENROLL_TIMEOUT_HIDDEN = 0,
ENROLL_TIMEOUT_MIN = 1,
ENROLL_TIMEOUT_DEFAULT = 15,
ENROLL_TIMEOUT_TYPE_MAX = UINT32_MAX,
};
bool secure_boot_enabled(void); bool secure_boot_enabled(void);
SecureBootMode secure_boot_mode(void); SecureBootMode secure_boot_mode(void);
EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path, bool force, secure_boot_enroll_action action); EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path, bool force,
secure_boot_enroll_action action, uint64_t timeout_sec);
typedef bool (*security_validator_t)( typedef bool (*security_validator_t)(
const void *ctx, const void *ctx,

View File

@ -630,7 +630,7 @@ static int parse_argv(int argc, char *argv[]) {
} }
case ARG_LIST_DEVICES: 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) if (r < 0)
return r; 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); 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_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_sysattr_bool(sd_device *device, const char *sysattr);
int device_get_devlink_priority(sd_device *device, int *ret); int device_get_devlink_priority(sd_device *device, int *ret);
int device_get_devnode_mode(sd_device *device, mode_t *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; 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) { int device_get_sysattr_bool(sd_device *device, const char *sysattr) {
const char *value; const char *value;
int r; int r;

View File

@ -9,6 +9,7 @@
#include "sd-id128.h" #include "sd-id128.h"
#include "sd-json.h" #include "sd-json.h"
#include "sd-varlink.h"
#include "alloc-util.h" #include "alloc-util.h"
#include "ask-password-api.h" #include "ask-password-api.h"
@ -79,6 +80,8 @@
#include "tpm2-pcr.h" #include "tpm2-pcr.h"
#include "tpm2-util.h" #include "tpm2-util.h"
#include "utf8.h" #include "utf8.h"
#include "varlink-io.systemd.Repart.h"
#include "varlink-util.h"
#include "xattr-util.h" #include "xattr-util.h"
/* If not configured otherwise use a minimal partition size of 10M */ /* 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 char *arg_generate_crypttab = NULL;
static Set *arg_verity_settings = NULL; static Set *arg_verity_settings = NULL;
static bool arg_relax_copy_block_security = false; static bool arg_relax_copy_block_security = false;
static bool arg_varlink = false;
STATIC_DESTRUCTOR_REGISTER(arg_node, freep); STATIC_DESTRUCTOR_REGISTER(arg_node, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep); STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
@ -9163,7 +9167,7 @@ static int parse_argv(
break; break;
case ARG_LIST_DEVICES: 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) if (r < 0)
return r; return r;
@ -9347,6 +9351,14 @@ static int parse_argv(
if (arg_append_fstab && !arg_generate_fstab) 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)); 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_certificate = TAKE_PTR(certificate);
*ret_private_key = TAKE_PTR(private_key); *ret_private_key = TAKE_PTR(private_key);
*ret_ui = TAKE_PTR(ui); *ret_ui = TAKE_PTR(ui);
@ -9771,6 +9783,106 @@ static int determine_auto_size(Context *c) {
return 0; 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[]) { static int run(int argc, char *argv[]) {
_cleanup_(X509_freep) X509 *certificate = NULL; _cleanup_(X509_freep) X509 *certificate = NULL;
_cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL; _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL;
@ -9787,6 +9899,13 @@ static int run(int argc, char *argv[]) {
if (r <= 0) if (r <= 0)
return r; return r;
#if HAVE_LIBCRYPTSETUP
cryptsetup_enable_logging(NULL);
#endif
if (arg_varlink)
return vl_server();
r = parse_proc_cmdline_factory_reset(); r = parse_proc_cmdline_factory_reset();
if (r < 0) if (r < 0)
return r; return r;
@ -9795,10 +9914,6 @@ static int run(int argc, char *argv[]) {
if (r < 0) if (r < 0)
return r; return r;
#if HAVE_LIBCRYPTSETUP
cryptsetup_enable_logging(NULL);
#endif
if (arg_image) { if (arg_image) {
assert(!arg_root); assert(!arg_root);

View File

@ -2,17 +2,51 @@
#include "sd-device.h" #include "sd-device.h"
#include "alloc-util.h"
#include "ansi-color.h" #include "ansi-color.h"
#include "blockdev-list.h" #include "blockdev-list.h"
#include "blockdev-util.h" #include "blockdev-util.h"
#include "device-private.h"
#include "device-util.h" #include "device-util.h"
#include "strv.h" #include "strv.h"
#include "terminal-util.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; _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
int r; 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) if (sd_device_enumerator_new(&e) < 0)
return log_oom(); return log_oom();
@ -35,6 +69,19 @@ int blockdev_list(BlockDevListFlags flags) {
continue; 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)) { if (FLAGS_SET(flags, BLOCKDEV_LIST_IGNORE_ZRAM)) {
r = device_sysname_startswith(dev, "zram"); r = device_sysname_startswith(dev, "zram");
if (r < 0) { 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;
}
}
if (FLAGS_SET(flags, BLOCKDEV_LIST_SHOW_SYMLINKS)) {
_cleanup_strv_free_ char **list = NULL; _cleanup_strv_free_ char **list = NULL;
if (FLAGS_SET(flags, BLOCKDEV_LIST_SHOW_SYMLINKS)) {
FOREACH_DEVICE_DEVLINK(dev, l) FOREACH_DEVICE_DEVLINK(dev, sl)
if (strv_extend(&list, l) < 0) if (strv_extend(&list, sl) < 0)
return log_oom(); return log_oom();
strv_sort(list); strv_sort(list);
}
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) STRV_FOREACH(i, list)
printf("%s%s%s%s\n", on_tty() ? " " : "", ansi_grey(), *i, ansi_normal()); 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; return 0;
} }

View File

@ -4,10 +4,27 @@
#include "forward.h" #include "forward.h"
typedef enum BlockDevListFlags { typedef enum BlockDevListFlags {
BLOCKDEV_LIST_SHOW_SYMLINKS = 1 << 0, BLOCKDEV_LIST_SHOW_SYMLINKS = 1 << 0, /* Pick up symlinks to block devices too */
BLOCKDEV_LIST_REQUIRE_PARTITION_SCANNING = 1 << 1, BLOCKDEV_LIST_REQUIRE_PARTITION_SCANNING = 1 << 1, /* Only consider block devices with partition scanning */
BLOCKDEV_LIST_IGNORE_ZRAM = 1 << 2, BLOCKDEV_LIST_IGNORE_ZRAM = 1 << 2, /* Ignore ZRAM */
BLOCKDEV_LIST_REQUIRE_LUKS = 1 << 3, 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; } 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.Network.c',
'varlink-io.systemd.PCRExtend.c', 'varlink-io.systemd.PCRExtend.c',
'varlink-io.systemd.PCRLock.c', 'varlink-io.systemd.PCRLock.c',
'varlink-io.systemd.Repart.c',
'varlink-io.systemd.Resolve.c', 'varlink-io.systemd.Resolve.c',
'varlink-io.systemd.Resolve.Monitor.c', 'varlink-io.systemd.Resolve.Monitor.c',
'varlink-io.systemd.Udev.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; break;
case ARG_LIST_DEVICES: 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) if (r < 0)
return r; 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 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" OFFLINE="yes"
run_testcases run_testcases

View File

@ -651,6 +651,15 @@ units = [
'conditions' : ['ENABLE_REPART'], 'conditions' : ['ENABLE_REPART'],
'symlinks' : ['sysinit.target.wants/', 'initrd-root-fs.target.wants/'], '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', 'file' : 'systemd-resolved.service.in',
'conditions' : ['ENABLE_RESOLVE'], '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