mirror of
https://github.com/systemd/systemd.git
synced 2026-08-03 22:50:29 +00:00
repart: add Varlink call that runs repart's engine
This commit is contained in:
@@ -9830,7 +9830,12 @@ done:
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int determine_auto_size(Context *c, uint64_t *ret) {
|
||||
static int determine_auto_size(
|
||||
Context *c,
|
||||
int level,
|
||||
bool ignore_allocated, /* If true, determines unallocated space needed */
|
||||
uint64_t *ret) {
|
||||
|
||||
uint64_t sum;
|
||||
|
||||
assert(c);
|
||||
@@ -9847,16 +9852,21 @@ static int determine_auto_size(Context *c, uint64_t *ret) {
|
||||
if (m > UINT64_MAX - sum)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "Image would grow too large, refusing.");
|
||||
|
||||
if (ignore_allocated && PARTITION_EXISTS(p))
|
||||
m = LESS_BY(m, p->current_size + p->current_padding);
|
||||
|
||||
sum += m;
|
||||
}
|
||||
|
||||
if (c->total != UINT64_MAX)
|
||||
/* Image already allocated? Then show its size. */
|
||||
log_info("Automatically determined minimal disk image size as %s, current block device/image size is %s.",
|
||||
log_full(level,
|
||||
"Automatically determined minimal disk image size as %s, current block device/image size is %s.",
|
||||
FORMAT_BYTES(sum), FORMAT_BYTES(c->total));
|
||||
else
|
||||
/* If the image is being created right now, then it has no previous size, suppress any comment about it hence. */
|
||||
log_info("Automatically determined minimal disk image size as %s.",
|
||||
log_full(level,
|
||||
"Automatically determined minimal disk image size as %s.",
|
||||
FORMAT_BYTES(sum));
|
||||
|
||||
if (ret)
|
||||
@@ -9987,6 +9997,162 @@ static int vl_method_list_candidate_devices(
|
||||
return sd_varlink_reply(link, v);
|
||||
}
|
||||
|
||||
static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_empty_mode, EmptyMode, empty_mode_from_string);
|
||||
|
||||
typedef struct RunParameters {
|
||||
char *node;
|
||||
EmptyMode empty;
|
||||
bool dry_run;
|
||||
sd_id128_t seed;
|
||||
char **definitions;
|
||||
} RunParameters;
|
||||
|
||||
static void run_parameters_done(RunParameters *p) {
|
||||
assert(p);
|
||||
|
||||
p->node = mfree(p->node);
|
||||
p->definitions = strv_free(p->definitions);
|
||||
}
|
||||
|
||||
static int vl_method_run(
|
||||
sd_varlink *link,
|
||||
sd_json_variant *parameters,
|
||||
sd_varlink_method_flags_t flags,
|
||||
void *userdata) {
|
||||
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "node", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(RunParameters, node), SD_JSON_NULLABLE },
|
||||
{ "empty", SD_JSON_VARIANT_STRING, json_dispatch_empty_mode, offsetof(RunParameters, empty), SD_JSON_MANDATORY },
|
||||
{ "seed", SD_JSON_VARIANT_STRING, sd_json_dispatch_id128, offsetof(RunParameters, seed), SD_JSON_NULLABLE },
|
||||
{ "dryRun", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(RunParameters, dry_run), SD_JSON_MANDATORY },
|
||||
{ "definitions", SD_JSON_VARIANT_ARRAY, json_dispatch_strv_path, offsetof(RunParameters, definitions), SD_JSON_MANDATORY|SD_JSON_STRICT },
|
||||
{}
|
||||
};
|
||||
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
_cleanup_(run_parameters_done) RunParameters p = {
|
||||
.empty = _EMPTY_MODE_INVALID,
|
||||
.dry_run = true,
|
||||
};
|
||||
r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
/* If no device node is specified, this is a dry run. Refuse if the caller claims otherwise. */
|
||||
if (!p.node && !p.dry_run)
|
||||
return sd_varlink_error_invalid_parameter_name(link, "dryRun");
|
||||
|
||||
_cleanup_(context_freep) Context* context = NULL;
|
||||
context = context_new(
|
||||
p.definitions,
|
||||
p.empty,
|
||||
p.dry_run,
|
||||
p.seed,
|
||||
/* certificate= */ NULL,
|
||||
/* private_key= */ NULL);
|
||||
if (!context)
|
||||
return log_oom();
|
||||
|
||||
r = context_read_seed(context, arg_root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = context_read_definitions(context);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (p.node) {
|
||||
context->node = TAKE_PTR(p.node);
|
||||
|
||||
r = context_load_partition_table(context);
|
||||
if (r == -EHWPOISON)
|
||||
return sd_varlink_error(link, "io.systemd.Repart.ConflictingDiskLabelPresent", NULL);
|
||||
} else
|
||||
r = context_load_fallback_metrics(context);
|
||||
if (r < 0)
|
||||
return r;
|
||||
context->from_scratch = r > 0; /* Starting from scratch */
|
||||
|
||||
r = context_open_copy_block_paths(context, (dev_t) -1);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = context_acquire_partition_uuids_and_labels(context);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = context_update_verity_size(context);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = context_minimize(context);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* If we have no node, just sum up how much space we need */
|
||||
if (!context->node) {
|
||||
/* Check if space issue is caused by the whole disk being too small */
|
||||
uint64_t size;
|
||||
r = determine_auto_size(context, LOG_DEBUG, /* ignore_allocated= */ false, &size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_varlink_replybo(
|
||||
link,
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("minimalSizeBytes", size));
|
||||
}
|
||||
|
||||
r = context_ponder(context);
|
||||
if (r == -ENOSPC) {
|
||||
/* Check if space issue is caused by the whole disk being too small */
|
||||
uint64_t size = UINT64_MAX;
|
||||
(void) determine_auto_size(context, LOG_DEBUG, /* ignore_allocated= */ false, &size);
|
||||
if (size != UINT64_MAX && context->total != UINT64_MAX && size > context->total)
|
||||
return sd_varlink_errorbo(
|
||||
link,
|
||||
"io.systemd.Repart.DiskTooSmall",
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("minimalSizeBytes", size),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("currentSizeBytes", context->total));
|
||||
|
||||
/* Or if the disk would fit, but theres's not enough unallocated space */
|
||||
uint64_t need_free = UINT64_MAX;
|
||||
(void) determine_auto_size(context, LOG_DEBUG, /* ignore_allocated= */ true, &need_free);
|
||||
return sd_varlink_errorbo(
|
||||
link,
|
||||
"io.systemd.Repart.InsufficientFreeSpace",
|
||||
JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("minimalSizeBytes", size, UINT64_MAX),
|
||||
JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("needFreeBytes", need_free, UINT64_MAX),
|
||||
JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("currentSizeBytes", context->total, UINT64_MAX));
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (p.dry_run) {
|
||||
uint64_t size;
|
||||
|
||||
/* If we are doing a dry-run, report the minimal size. */
|
||||
r = determine_auto_size(context, LOG_DEBUG, /* ignore_allocated= */ false, &size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_varlink_replybo(
|
||||
link,
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("minimalSizeBytes", size),
|
||||
JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("currentSizeBytes", context->total, UINT64_MAX));
|
||||
}
|
||||
|
||||
r = context_write_partition_table(context);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
context_disarm_auto_removal(context);
|
||||
|
||||
return sd_varlink_reply(link, NULL);
|
||||
}
|
||||
|
||||
static int vl_server(void) {
|
||||
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
|
||||
int r;
|
||||
@@ -10006,7 +10172,8 @@ static int vl_server(void) {
|
||||
|
||||
r = sd_varlink_server_bind_method_many(
|
||||
varlink_server,
|
||||
"io.systemd.Repart.ListCandidateDevices", vl_method_list_candidate_devices);
|
||||
"io.systemd.Repart.ListCandidateDevices", vl_method_list_candidate_devices,
|
||||
"io.systemd.Repart.Run", vl_method_run);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to bind Varlink methods: %m");
|
||||
|
||||
@@ -10221,12 +10388,12 @@ static int run(int argc, char *argv[]) {
|
||||
return r;
|
||||
|
||||
if (arg_node_none) {
|
||||
(void) determine_auto_size(context, /* ret= */ NULL);
|
||||
(void) determine_auto_size(context, LOG_INFO, /* ignore_allocated= */ false, /* ret= */ NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg_size_auto) {
|
||||
r = determine_auto_size(context, &arg_size);
|
||||
r = determine_auto_size(context, LOG_INFO, /* ignore_allocated= */ false, &arg_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -10251,7 +10418,7 @@ static int run(int argc, char *argv[]) {
|
||||
r = context_ponder(context);
|
||||
if (r == -ENOSPC) {
|
||||
/* When we hit space issues, tell the user the minimal size. */
|
||||
(void) determine_auto_size(context, /* ret= */ NULL);
|
||||
(void) determine_auto_size(context, LOG_INFO, /* ignore_allocated= */ false, /* ret= */ NULL);
|
||||
return r;
|
||||
}
|
||||
if (r < 0)
|
||||
|
||||
@@ -4,6 +4,35 @@
|
||||
|
||||
#include "varlink-io.systemd.Repart.h"
|
||||
|
||||
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("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));
|
||||
|
||||
static SD_VARLINK_DEFINE_METHOD(
|
||||
ListCandidateDevices,
|
||||
SD_VARLINK_FIELD_COMMENT("The device node path of the block device."),
|
||||
@@ -20,11 +49,39 @@ static SD_VARLINK_DEFINE_METHOD(
|
||||
SD_VARLINK_DEFINE_OUTPUT(sizeBytes, SD_VARLINK_INT, 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("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."),
|
||||
|
||||
Reference in New Issue
Block a user