From 1c76e204d3eb2e43918ddd18c9a93eed8facedfd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 12 Sep 2025 21:23:57 +0200 Subject: [PATCH] repart: send out progress information via varlink (if more flag is given) And while we are at it, also send it out via sd_notify() --- src/repart/repart.c | 134 +++++++++++++++++++++++-- src/shared/varlink-io.systemd.Repart.c | 26 ++++- 2 files changed, 153 insertions(+), 7 deletions(-) diff --git a/src/repart/repart.c b/src/repart/repart.c index f871bd06434..d8a602d6fa8 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -7,6 +7,7 @@ #include #include +#include "sd-daemon.h" #include "sd-id128.h" #include "sd-json.h" #include "sd-varlink.h" @@ -233,6 +234,24 @@ STATIC_DESTRUCTOR_REGISTER(arg_generate_fstab, freep); STATIC_DESTRUCTOR_REGISTER(arg_generate_crypttab, freep); STATIC_DESTRUCTOR_REGISTER(arg_verity_settings, set_freep); +typedef enum ProgressPhase { + PROGRESS_LOADING_DEFINITIONS, + PROGRESS_LOADING_TABLE, + PROGRESS_OPENING_COPY_BLOCK_SOURCES, + PROGRESS_ACQUIRING_PARTITION_LABELS, + PROGRESS_MINIMIZING, + PROGRESS_PLACING, + PROGRESS_WIPING_DISK, + PROGRESS_WIPING_PARTITION, + PROGRESS_COPYING_PARTITION, + PROGRESS_FORMATTING_PARTITION, + PROGRESS_ADJUSTING_PARTITION, + PROGRESS_WRITING_TABLE, + PROGRESS_REREADING_TABLE, + _PROGRESS_PHASE_MAX, + _PROGRESS_PHASE_INVALID = -EINVAL, +} ProgressPhase; + typedef struct FreeArea FreeArea; typedef enum EncryptMode { @@ -370,7 +389,11 @@ static Subvolume* subvolume_free(Subvolume *s) { DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(subvolume_hash_ops, char, path_hash_func, path_compare, Subvolume, subvolume_free); +typedef struct Context Context; + typedef struct Partition { + Context *context; + char *definition_path; char **drop_in_files; @@ -471,7 +494,7 @@ struct FreeArea { uint64_t allocated; }; -typedef struct Context { +struct Context { char **definitions; LIST_HEAD(Partition, partitions); @@ -498,7 +521,9 @@ typedef struct Context { X509 *certificate; EVP_PKEY *private_key; -} Context; + + sd_varlink *link; /* If 'more' is used on the Varlink call, we'll send progress info over this link */ +}; static const char *empty_mode_table[_EMPTY_MODE_MAX] = { [EMPTY_UNSET] = "unset", @@ -535,11 +560,28 @@ static const char *minimize_mode_table[_MINIMIZE_MODE_MAX] = { [MINIMIZE_GUESS] = "guess", }; +static const char *progress_phase_table[_PROGRESS_PHASE_MAX] = { + [PROGRESS_LOADING_DEFINITIONS] = "loading-definitions", + [PROGRESS_LOADING_TABLE] = "loading-table", + [PROGRESS_OPENING_COPY_BLOCK_SOURCES] = "opening-copy-block-sources", + [PROGRESS_ACQUIRING_PARTITION_LABELS] = "acquiring-partition-labels", + [PROGRESS_MINIMIZING] = "minimizing", + [PROGRESS_PLACING] = "placing", + [PROGRESS_WIPING_DISK] = "wiping-disk", + [PROGRESS_WIPING_PARTITION] = "wiping-partition", + [PROGRESS_COPYING_PARTITION] = "copying-partition", + [PROGRESS_FORMATTING_PARTITION] = "formatting-partition", + [PROGRESS_ADJUSTING_PARTITION] = "adjusting-partition", + [PROGRESS_WRITING_TABLE] = "writing-table", + [PROGRESS_REREADING_TABLE] = "rereading-table", +}; + DEFINE_PRIVATE_STRING_TABLE_LOOKUP(empty_mode, EmptyMode); DEFINE_PRIVATE_STRING_TABLE_LOOKUP(append_mode, AppendMode); DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(encrypt_mode, EncryptMode, ENCRYPT_KEY_FILE); DEFINE_PRIVATE_STRING_TABLE_LOOKUP(verity_mode, VerityMode); DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(minimize_mode, MinimizeMode, MINIMIZE_BEST); +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(progress_phase, ProgressPhase); static uint64_t round_down_size(uint64_t v, uint64_t p) { return (v / p) * p; @@ -603,7 +645,7 @@ static int calculate_verity_hash_size( return 0; } -static Partition *partition_new(void) { +static Partition *partition_new(Context *c) { Partition *p; p = new(Partition, 1); @@ -611,6 +653,7 @@ static Partition *partition_new(void) { return NULL; *p = (Partition) { + .context = c, .weight = 1000, .padding_weight = 0, .current_size = UINT64_MAX, @@ -871,6 +914,8 @@ static Context* context_free(Context *context) { X509_free(context->certificate); EVP_PKEY_free(context->private_key); + context->link = sd_varlink_unref(context->link); + return mfree(context); } @@ -2655,6 +2700,48 @@ static MakeFileSystemFlags partition_mkfs_flags(const Partition *p) { return flags; } +static int context_notify( + Context *c, + ProgressPhase phase, + const char *object, + unsigned percent) { + + int r; + + assert(c); + assert(phase >= 0); + assert(phase < _PROGRESS_PHASE_MAX); + + /* Send progress information, via sd_notify() and via varlink (if client asked for it by setting "more" flag) */ + + _cleanup_free_ char *n = NULL; + if (asprintf(&n, + "STATUS=Phase %1$s\n" + "X_SYSTEMD_PHASE=%1$s", + progress_phase_to_string(phase)) < 0) + return log_oom_debug(); + + if (percent != UINT_MAX) + if (strextendf(&n, "\nX_SYSTEMD_PHASE_PROGRESS=%u", percent) < 0) + return log_oom_debug(); + + r = sd_notify(/* unset_environment= */ false, n); + if (r < 0) + log_debug_errno(r, "Failed to send sd_notify() progress notification, ignoring: %m"); + + if (c->link) { + r = sd_varlink_notifybo( + c->link, + SD_JSON_BUILD_PAIR("phase", JSON_BUILD_STRING_UNDERSCORIFY(progress_phase_to_string(phase))), + JSON_BUILD_PAIR_STRING_NON_EMPTY("object", object), + JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("progress", percent, UINT_MAX)); + if (r < 0) + log_debug_errno(r, "Failed to send varlink notify progress notification, ignoring: %m"); + } + + return 0; +} + static int partition_read_definition( Context *c, Partition *p, @@ -3110,7 +3197,7 @@ static int context_copy_from_one(Context *context, const char *src) { if (partition_type_exclude(&type)) continue; - np = partition_new(); + np = partition_new(context); if (!np) return log_oom(); @@ -3231,6 +3318,8 @@ static int context_read_definitions(Context *context) { assert(context); + (void) context_notify(context, PROGRESS_LOADING_DEFINITIONS, /* object= */ NULL, UINT_MAX); + dirs = (const char* const*) (context->definitions ?: CONF_PATHS_STRV("repart.d")); r = conf_files_list_strv( @@ -3245,7 +3334,7 @@ static int context_read_definitions(Context *context) { STRV_FOREACH(f, files) { _cleanup_(partition_freep) Partition *p = NULL; - p = partition_new(); + p = partition_new(context); if (!p) return log_oom(); @@ -3440,6 +3529,8 @@ static int context_load_partition_table(Context *context) { assert(context->end == UINT64_MAX); assert(context->total == UINT64_MAX); + context_notify(context, PROGRESS_LOADING_TABLE, /* object= */ NULL, UINT_MAX); + c = fdisk_new_context(); if (!c) return log_oom(); @@ -3709,7 +3800,7 @@ static int context_load_partition_table(Context *context) { if (!found) { _cleanup_(partition_freep) Partition *np = NULL; - np = partition_new(); + np = partition_new(context); if (!np) return log_oom(); @@ -4570,6 +4661,8 @@ static int context_wipe_and_discard(Context *context) { if (partition_type_defer(&p->type)) continue; + (void) context_notify(context, PROGRESS_WIPING_PARTITION, p->definition_path, UINT_MAX); + r = context_wipe_partition(context, p); if (r < 0) return r; @@ -5577,6 +5670,8 @@ static int progress_bytes(uint64_t n_bytes, uint64_t bps, void *userdata) { p->last_percent = percent; + (void) context_notify(p->context, PROGRESS_COPYING_PARTITION, p->definition_path, percent); + return 0; } @@ -5606,6 +5701,8 @@ static int context_copy_blocks(Context *context) { if (p->copy_blocks_fd < 0) continue; + (void) context_notify(context, PROGRESS_COPYING_PARTITION, p->definition_path, UINT_MAX); + assert(p->new_size != UINT64_MAX); size_t extra = p->encrypt != ENCRYPT_OFF ? LUKS2_METADATA_KEEP_FREE : 0; @@ -6580,6 +6677,8 @@ static int context_mkfs(Context *context) { if (p->copy_blocks_fd >= 0) continue; + (void) context_notify(context, PROGRESS_FORMATTING_PARTITION, p->definition_path, UINT_MAX); + assert(p->offset != UINT64_MAX); assert(p->new_size != UINT64_MAX); assert(p->new_size >= (p->encrypt != ENCRYPT_OFF ? LUKS2_METADATA_KEEP_FREE : 0)); @@ -6840,6 +6939,8 @@ static int context_acquire_partition_uuids_and_labels(Context *context) { continue; } + (void) context_notify(context, PROGRESS_ACQUIRING_PARTITION_LABELS, p->definition_path, UINT_MAX); + if (!sd_id128_is_null(p->current_uuid)) p->new_uuid = uuid = p->current_uuid; /* Never change initialized UUIDs */ else if (p->new_uuid_is_set) @@ -6967,6 +7068,8 @@ static int context_mangle_partitions(Context *context) { if (partition_type_defer(&p->type)) continue; + (void) context_notify(context, PROGRESS_ADJUSTING_PARTITION, p->definition_path, UINT_MAX); + assert(p->new_size != UINT64_MAX); assert(p->offset != UINT64_MAX); assert(p->partno != UINT64_MAX); @@ -7264,6 +7367,9 @@ static int context_write_partition_table(Context *context) { log_info("Applying changes to %s.", context->node); if (context->from_scratch && context->empty != EMPTY_CREATE) { + + (void) context_notify(context, PROGRESS_WIPING_DISK, /* object= */ NULL, UINT_MAX); + /* Erase everything if we operate from scratch, except if the image was just created anyway, and thus is definitely empty. */ r = context_wipe_range(context, 0, context->total); if (r < 0) @@ -7306,6 +7412,8 @@ static int context_write_partition_table(Context *context) { log_info("Writing new partition table."); + (void) context_notify(context, PROGRESS_WRITING_TABLE, /* object= */ NULL, UINT_MAX); + r = fdisk_write_disklabel(context->fdisk_context); if (r < 0) return log_error_errno(r, "Failed to write partition table: %m"); @@ -7317,6 +7425,8 @@ static int context_write_partition_table(Context *context) { return log_error_errno(capable, "Failed to check if block device supports partition scanning: %m"); else if (capable > 0) { log_info("Informing kernel about changed partitions..."); + (void) context_notify(context, PROGRESS_REREADING_TABLE, /* object= */ NULL, UINT_MAX); + r = reread_partition_table_fd(fdisk_get_devfd(context->fdisk_context), /* flags= */ 0); if (r < 0) return log_error_errno(r, "Failed to reread partition table: %m"); @@ -7809,6 +7919,9 @@ static int context_open_copy_block_paths( assert(context); + if (!context->partitions) + return 0; + LIST_FOREACH(partitions, p, context->partitions) { _cleanup_close_ int source_fd = -EBADF; _cleanup_free_ char *opened = NULL; @@ -7855,6 +7968,8 @@ static int context_open_copy_block_paths( } else continue; + (void) context_notify(context, PROGRESS_OPENING_COPY_BLOCK_SOURCES, p->definition_path, UINT_MAX); + if (S_ISDIR(st.st_mode)) { _cleanup_free_ char *bdev = NULL; dev_t devt; @@ -8291,6 +8406,8 @@ static int context_minimize(Context *context) { if (!partition_needs_populate(p)) continue; + (void) context_notify(context, PROGRESS_MINIMIZING, p->definition_path, UINT_MAX); + assert(!p->copy_blocks_path); (void) partition_hint(p, context->node, &hint); @@ -9879,6 +9996,8 @@ static int context_ponder(Context *context) { assert(context); + (void) context_notify(context, PROGRESS_PLACING, /* object= */ NULL, UINT_MAX); + /* First try to fit new partitions in, dropping by priority until it fits */ for (;;) { uint64_t largest_free_area; @@ -10056,6 +10175,9 @@ static int vl_method_run( if (!context) return log_oom(); + if (FLAGS_SET(flags, SD_VARLINK_METHOD_MORE)) + context->link = sd_varlink_ref(link); + r = context_read_seed(context, arg_root); if (r < 0) return r; diff --git a/src/shared/varlink-io.systemd.Repart.c b/src/shared/varlink-io.systemd.Repart.c index c3b009c56fe..ce46eb0b1f2 100644 --- a/src/shared/varlink-io.systemd.Repart.c +++ b/src/shared/varlink-io.systemd.Repart.c @@ -4,6 +4,22 @@ #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"), @@ -31,7 +47,13 @@ static SD_VARLINK_DEFINE_METHOD_FULL( 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_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, @@ -72,6 +94,8 @@ SD_VARLINK_DEFINE_INTERFACE( 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, -- 2.47.3